Python权威指南之如何使用静态类或抽象函数 分类: python学习 2015-05-12 18:18 57人阅读 评论(0) 收藏
2015-05-12 18:18
881 查看
来源:http://www.xdarui.com/archives/261.html
原文http://julien.danjou.info/blog/2013/guide-python-static-class-abstract-methods
代码审查对发现人们很难理解的问题来说绝对是一种很赞的方式。我(Julien Danjou)最近在做OpenStack patches校对的时候发现人们不能够正确的理解与使用Python提供的不同的函数修饰。所以我这里提供一个链接以便下次review的时候帖出。
这里Python告诉了我们
因为它并未绑定到任何一个
可以运行了!我们在调用的时候给第一个参数传入了它的实例,所以一切都正常了。但是你不得不诚认,这样调用不是很方便。每次调用这个方法的时候我们都得引用一个类,而且我们也不知道这个对象是属于哪个类,这样肯定是不是长久之计。
所以Python帮我们做了这些事情,给所有
正如我们所期望,我们必没有给
事实上,你甚至都不需要为你的
显然,我仍有一个我们这个对象的引用,我们随时可以通过它找回。
在Python3中,直接使用类中的方法不再视为未绑定方法,而是一个简单的函数,而只是在需要的时候才绑定到对象上。所以基本原则没变只是简化了模型。
这种情景下,写了这样的非静态方法
Python不会给每一个
无论你是如何使用类方法,它始终都是连接到类,而且第一个参数是类本身(记住类也是对象)。我们何时使用这种方法?类方法大多有这两种用法:
工厂方法,用于创建一个类的实例比如一些预处理。如果我们用
静态方法之前的调用:如果你把静态方法分散成很多静态方法,你不能直接硬编码类名而应该用类方法。使用这种方式申明一个类方法,
任何从
这里有一个简单的办法可以在类被实例化后触发它,使用Python提供的abc模块。
使用
请记住,申明类时应先申明成抽象类,并且不要把类的原型写死。这意味着,抽象方法必需得实现,但我可以在实现抽象方法时可以任意指定参数列表。
当
这是正确的而且也实现了抽象类
不要误读成这个:如果你尝试去强迫子类实现
抽象类中实现抽象方法?是的!Python中抽象方法与Java相反。你可以在抽象方法中加入代码并通过
这种情况下,所有从
原文http://julien.danjou.info/blog/2013/guide-python-static-class-abstract-methods
代码审查对发现人们很难理解的问题来说绝对是一种很赞的方式。我(Julien Danjou)最近在做OpenStack patches校对的时候发现人们不能够正确的理解与使用Python提供的不同的函数修饰。所以我这里提供一个链接以便下次review的时候帖出。
Python中方法是怎么工作的
一个方法就是一个函数,它依付于一个类之上。你可以像这样去定义和使用:>>> class Pizza(object): ... def __init__(self, size): ... self.size = size ... def get_size(self): ... return self.size ... >>> Pizza.get_size <unbound method Pizza.get_size>
这里Python告诉了我们
Pizza这个类中的
get_size方法没绑定,这是什么意思?我们尝试去调用下就知道了:
>>> Pizza.get_size() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unbound method get_size() must be called with Pizza instance as first argument (got nothing instead)
因为它并未绑定到任何一个
Pizza的实例中去,而方法的第一个参数希望接收一个实例。(Python2中实例必需是对应的那个类,而Python3中可以是任何东西)让我这样试试:
>>> Pizza.get_size(Pizza(42)) 42
可以运行了!我们在调用的时候给第一个参数传入了它的实例,所以一切都正常了。但是你不得不诚认,这样调用不是很方便。每次调用这个方法的时候我们都得引用一个类,而且我们也不知道这个对象是属于哪个类,这样肯定是不是长久之计。
所以Python帮我们做了这些事情,给所有
Pizza的实例绑定所有它的方法。这意味着
get_size是
Pizza实例的已绑定方法:方法的第一个参数是实例它本身。
>>> Pizza(42).get_size <bound method Pizza.get_size of <__main__.Pizza object at 0x7f3138827910>> >>> Pizza(42).get_size() 42
正如我们所期望,我们必没有给
get_size()方法传入任何参数,因为它已经绑定了,
Pizza实例自身会自动设置成方法的第一个参数。这可以证明:
>>> m = Pizza(42).get_size >>> m() 42
事实上,你甚至都不需要为你的
Pizza对象保存引用,因其方法与对象绑定了,所以其方法本身就够了。但是如果我们想知道这个方法倒底绑定到了哪个对象上该怎么做?这有一个小技巧:
>>> m = Pizza(42).get_size >>> m.__self__ <__main__.Pizza object at 0x7f3138827910> >>> # You could guess, look at this: ... >>> m == m.__self__.get_size True
显然,我仍有一个我们这个对象的引用,我们随时可以通过它找回。
在Python3中,直接使用类中的方法不再视为未绑定方法,而是一个简单的函数,而只是在需要的时候才绑定到对象上。所以基本原则没变只是简化了模型。
>>> class Pizza(object): ... def __init__(self, size): ... self.size = size ... def get_size(self): ... return self.size ... >>> Pizza.get_size <function Pizza.get_size at 0x7f307f984dd0>
静态方法
静态方法是方法中的一个特例。有时,你写了一段属于某个类的代码,但是它并未使用这个类。比如:class Pizza(object): @staticmethod def mix_ingredients(x, y): return x + y def cook(self): return self.mix_ingredients(self.cheese, self.vegetables)
这种情景下,写了这样的非静态方法
mix_ingredients也能工作,但会传递一个并不会使用的参数
self。这里个注解
@staticmethod可以帮我们做这些情况:
Python不会给每一个
Pizza实例都增加一个绑定的方法。绑定方法也是一个函数,所以创建它们也有额外的开销。通过静态方法可以避免这些开销:
>>> Pizza().cook is Pizza().cook False >>> Pizza().mix_ingredients is Pizza.mix_ingredients True >>> Pizza().mix_ingredients is Pizza().mix_ingredients True
类方法
说到这,倒底什么是类方法?类方法是指不是绑定在对象上而是类上的方法!。>>> class Pizza(object): ... radius = 42 ... @classmethod ... def get_radius(cls): ... return cls.radius ... >>> >>> Pizza.get_radius <bound method type.get_radius of <class '__main__.Pizza'>> >>> Pizza().get_radius <bound method type.get_radius of <class '__main__.Pizza'>> >>> Pizza.get_radius is Pizza().get_radius True >>> Pizza.get_radius() 42
无论你是如何使用类方法,它始终都是连接到类,而且第一个参数是类本身(记住类也是对象)。我们何时使用这种方法?类方法大多有这两种用法:
工厂方法,用于创建一个类的实例比如一些预处理。如果我们用
@staticmethod,我们就得将类名
Pizza硬编码进函数,任何继承自
Pizza的类都无法使用工厂方法的自身用处。
class Pizza(object): def __init__(self, ingredients): self.ingredients = ingredients @classmethod def from_fridge(cls, fridge): return cls(fridge.get_cheese() + fridge.get_vegetables())
静态方法之前的调用:如果你把静态方法分散成很多静态方法,你不能直接硬编码类名而应该用类方法。使用这种方式申明一个类方法,
Pizza类名不会被直接使用,而且继承的子类和重载父类都会很好的工作。
class Pizza(object): def __init__(self, radius, height): self.radius = radius self.height = height @staticmethod def compute_circumference(radius): return math.pi * (radius ** 2) @classmethod def compute_volume(cls, height, radius): return height * cls.compute_circumference(radius) def get_volume(self): return self.compute_volume(self.height, self.radius)
抽象方法
抽象方法是基类中定义的方法,但却没有任何实现。在Java中,可以把方法申明成一个接口。而在Python中实现一个抽象方法的最简便方法是:class Pizza(object): def get_radius(self): raise NotImplementedError
任何从
Pizza继承下来的子类都必需实现
get_radius方法,否则就会产生一个错误。这种方式实现在抽象方法也有缺点,如果你写了一个类继承自
Pizza但却忘了实现
get_radius时,只有当你用到了那个方法时才会抛错。
>>> Pizza() <__main__.Pizza object at 0x7fb747353d90> >>> Pizza().get_radius() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 3, in get_radius NotImplementedError
这里有一个简单的办法可以在类被实例化后触发它,使用Python提供的abc模块。
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def get_radius(self): """Method that should do something."""
使用
abc以及其特定的方法,一旦你尝试去实例化
BasePizza类或任意从其继承下来的类的时候都会得到一个错误。
>>> BasePizza() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class BasePizza with abstract methods get_radius
混搭使用静态、类以及抽象方法
当创建类及继承时,你就得使用混搭这些方式的方法了。这里一些提示。请记住,申明类时应先申明成抽象类,并且不要把类的原型写死。这意味着,抽象方法必需得实现,但我可以在实现抽象方法时可以任意指定参数列表。
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def get_ingredients(self): """Returns the ingredient list.""" class Calzone(BasePizza): def get_ingredients(self, with_egg=False): egg = Egg() if with_egg else None return self.ingredients + egg
当
Calzone继承
BasePizza得按要求实现对应的接口,这非常有用。这意味着,我们可以实现成一个类或一个静态方法,比如:
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def get_ingredients(self): """Returns the ingredient list.""" class DietPizza(BasePizza): @staticmethod def get_ingredients(): return None
这是正确的而且也实现了抽象类
BasePizza所有要求。事实上一个实现细节是
get_ingredients方法返回结果时无需关心对象,不用关心有何约束。因此,你不能强制要求抽象方法或类的实现是有规律的,或者说不应该这样做。从Python3开始(Python2不会生效,见issue5867),可以在
@abstractmethod之上使用
@staticmethod和
@classmethod。
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta ingredient = ['cheese'] @classmethod @abc.abstractmethod def get_ingredients(cls): """Returns the ingredient list.""" return cls.ingredients
不要误读成这个:如果你尝试去强迫子类实现
get_ingredients作为类的方法,你就错了。这只是意味着你在
BasePizza类中,实现
get_ingredients只是一个类方法。
抽象类中实现抽象方法?是的!Python中抽象方法与Java相反。你可以在抽象方法中加入代码并通过
super()调用:
import abc class BasePizza(object): __metaclass__ = abc.ABCMeta default_ingredients = ['cheese'] @classmethod @abc.abstractmethod def get_ingredients(cls): """Returns the ingredient list.""" return cls.default_ingredients class DietPizza(BasePizza): def get_ingredients(self): return ['egg'] + super(DietPizza, self).get_ingredients()
这种情况下,所有从
BasePizza类中继承下来的pizza都得实现
get_ingredients方法,但是我们可以使用
super()通过默认机制获取配料表。
相关文章推荐
- 使用set()求出列表交集 分类: python基础学习 2013-06-16 17:00 241人阅读 评论(0) 收藏
- python数据持久存储:pickle模块的基本使用 分类: python python基础学习 python 小练习 2013-06-17 14:41 209人阅读 评论(0) 收藏
- Python中单/双下划线使用 分类: python python基础学习 2013-09-02 21:31 1764人阅读 评论(0) 收藏
- 对象使用self注意事项 分类: python基础学习 2013-09-03 09:52 218人阅读 评论(0) 收藏
- 使用os.popen/commands.getoutput查询指定端口号的服务 分类: python 小练习 open()文件读写 python基础学习 2013-09-17 17:49 798人阅读 评论(0) 收藏
- 使用生成器返回fibs列表 分类: python Module python基础学习 2013-10-28 18:19 283人阅读 评论(0) 收藏
- if ...__name__使用技巧总结 分类: python基础学习 python Module python 2013-11-01 14:51 262人阅读 评论(0) 收藏
- 使用文件进行优化 分类: python 小练习 divide into python python基础学习 2014-01-01 14:13 228人阅读 评论(0) 收藏
- 使用locals()获得类,进行分发 分类: python 小练习 divide into python python基础学习 2014-02-21 14:51 217人阅读 评论(0) 收藏
- 使用getattr() 分类: python基础学习 divide into python 2014-02-24 15:50 198人阅读 评论(0) 收藏
- Python 使用 Xlrd/xlwt 操作 Excel 分类: python基础学习 2014-03-17 12:06 958人阅读 评论(0) 收藏
- 使用类、类属性、对象属性 分类: python基础学习 2014-03-19 16:22 220人阅读 评论(0) 收藏
- Python权威指南之如何使用静态类或抽象函数
- python如何使用 os.path.exists()--Learning from stackoverflow 分类: python 2015-04-23 20:48 139人阅读 评论(0) 收藏
- Qt简介以及如何配置Qt使用VS2010进行开发 分类: QT学习实践 2015-05-05 16:02 34人阅读 评论(0) 收藏
- Python权威指南之如何使用静态类或抽象函数
- Python生成8位随机字符串的一些方法 分类: python学习 2015-04-28 20:00 62人阅读 评论(0) 收藏
- 生成二维码报错:IOError: encoder zip not available 分类: python学习 2015-05-06 19:43 107人阅读 评论(0) 收藏
- Python的静态方法和类成员方法 分类: python学习 2015-05-07 19:45 40人阅读 评论(0) 收藏
- 如何辨别密码安全糟糕的网站 分类: 软件插件学习 2015-05-08 11:32 54人阅读 评论(0) 收藏