python高级(类的命名空间(实例化属性)、抽象基类、abc模块(接口继承与归一化设计(工厂方法模式)))

类的命名空间
下面两条语句大致等价:
def foo(x): return x * x 
foo = lambda x: x * x 
它们都创建一个返回参数平方的函数,并将这个函数关联到变量foo。可以在全局(模块)作用域内定义名称foo,也可以在函数或方法内定义。定义类时情况亦如此:在class语句中定义的代码都是在一个特殊的命名空间(类的命名空间)内执行的,而类的所有成员都可访问这个命名空间。类定义其实就是要执行的代码段,并非所有的Python程序员都知道这一点,但知道这一点很有帮助。例如,在类定义中,并非只能包含def语句。对于成员变量(属性),有些语言支持多种私有程度。
>>> class C: 
...       print('Class C being defined...') 
... 
Class C being defined... 
>>> 
这有点傻,但请看下面的代码:
class MemberCounter: 
    members = 0 
    def init(self): 
        MemberCounter.members += 1 
>>> m1 = MemberCounter() 
>>> m1.init() 
>>> MemberCounter.members 

>>> m2 = MemberCounter() 
>>> m2.init() 
>>> MemberCounter.members 

上述代码在类作用域内定义了一个变量,所有的成员(实例)都可访问它,这里使用它来计算类实例的数量。注意到这里使用了init来初始化所有实例,这个初始化过程自动化,也就是将init转换为合适的构造函数。
每个实例都可访问这个类作用域内的变量,就像方法一样。
>>> m1.members 

>>> m2.members 

如果你在一个实例中给属性members赋值,结果将如何呢?
>>> m1.members = 'Two' 
>>> m1.members 
'Two' 
>>> m2.members 

新值被写入m1的一个属性中,这个属性遮住了类级变量。这类似于“遮盖的问题”所讨论的,函数中局部变量和全局变量之间的关系。

 

抽象基类
然而,有比手工检查各个方法更好的选择。在历史上的大部分时间内,Python几乎都只依赖于鸭子类型,即假设所有对象都能完成其工作,同时偶尔使用hasattr来检查所需的方法是否存在。很多其他语言(如Java和Go)都采用显式指定接口的理念,而有些第三方模块提供了这种理念的各种实现。最终,Python通过引入模块abc提供了官方解决方案。这个模块为所谓的抽象基类提供了支持。一般而言,抽象类是不能(至少是不应该)实例化的类,其职责是定义子类应实现的一组抽象方法。下面是一个简单的示例:
from abc import ABC, abstractmethod 
class Talker(ABC): 
   @abstractmethod 
   def talk(self): 
       pass 
形如@this的东西被称为装饰器,这里的要点是你使用@abstractmethod来将方法标记为抽象的——在子类中必须实现的方法。

如果你使用的是较旧的Python版本,将无法在模块abc中找到ABC类。在这种情况下,需要导入ABCMeta,并在类定义开头包含代码行__metaclass__ = ABCMeta(紧跟在class语句后面并缩进)。如果你使用的是3.4之前的Python 3版本,也可使用Talker(metaclass=ABCMeta)代替Talker(ABC)。

抽象类(即包含抽象方法的类)最重要的特征是不能实例化。
>>> Talker() 
Traceback (most recent call last): 
 File "<stdin>", line 1, in <module> 
TypeError: Can't instantiate abstract class Talker with abstract methods talk 
假设像下面这样从它派生出一个子类:
class Knigget(Talker): 
    pass 
由于没有重写方法talk,因此这个类也是抽象的,不能实例化。如果你试图这样做,将出现类似于前面的错误消息。然而,你可重新编写这个类,使其实现要求的方法。
class Knigget(Talker): 
   def talk(self): 
       print("Ni!") 
现在实例化它没有任何问题。这是抽象基类的主要用途,而且只有在这种情形下使用isinstance才是妥当的:如果先检查给定的实例确实是Talker对象,就能相信这个实例在需要的情况下有方法talk。
>>> k = Knigget() 

>>> isinstance(k, Talker) 
True 
>>> k.talk() 
Ni! 
然而,还缺少一个重要的部分——让isinstance的多态程度更高的部分。正如你看到的,抽象基类让我们能够本着鸭子类型的精神使用这种实例检查!我们不关心对象是什么,只关心对象能做什么(它实现了哪些方法)。因此,只要实现了方法talk,即便不是Talker的子类,依然能够通过类型检查。下面来创建另一个类。
class Herring: 
   def talk(self): 
      print("Blub.") 
这个类的实例能够通过是否为Talker对象的检查,可它并不是Talker对象。
>>> h = Herring() 
>>> isinstance(h, Talker) 
False 
诚然,你可从Talker派生出Herring,这样就万事大吉了,但Herring可能是从他人的模块中导入的。在这种情况下,就无法采取这样的做法。为解决这个问题,你可将Herring注册为Talker(而不从Herring和Talker派生出子类),这样所有的Herring对象都将被视为Talker对象。
>>> Talker.register(Herring) 
<class '__main__.Herring'> 
>>> isinstance(h, Talker) 
True 
>>> issubclass(Herring, Talker) 
True 
然而,这种做法存在一个缺点,就是直接从抽象类派生提供的保障没有了。
>>> class Clam: 
...        pass 
... 
>>> Talker.register(Clam) 
<class '__main__.Clam'> 
>>> issubclass(Clam, Talker) 
True 
>>> c = Clam() 
>>> isinstance(c, Talker) 
True 
>>> c.talk() 
Traceback (most recent call last): 
 File "<stdin>", line 1, in <module> 
AttributeError: 'Clam' object has no attribute 'talk'
换而言之,应将isinstance返回True视为一种意图表达。在这里,Clam有成为Talker的意图。
本着鸭子类型的精神,我们相信它能承担Talker的职责,但可悲的是它失败了。

 

接口继承与归一化设计(工厂方法模式

工厂方法模式:

在父类中继承abc模块,装饰abc抽象基类,但父类中不去实现其方法,而是去子类中实现,如果子类中没有完全继承实现父类中的方法,会报错,作用在于可以严格匹配一致。

import abc
class All_file(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def read(self):
        pass

    @abc.abstractmethod
    def write(self):
        pass

class Disk(All_file):
    def read(self):
        print('disk read')

    def write(self):
        print('disk write')

class Cdrom(All_file):
    def read(self):
        print('cdrom read')

    def write(self):
        print('cdrom write')


class Mem(All_file):
    def read(self):
        print('mem read')

    def write(self):
        print('mem write')

m1=Mem()
m1.read()
m1.write()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值