• Python中的对象行为与特殊方法(二)类型检查与抽象基类


    类型检查

    创建类的实例时,该实例的类型为类本身:

    class Foo(object):
        pass
    
    f = Foo()
    

    要测试实例是否属于某个类,可以使用type()内置函数:

    >>> type(f) == Foo
    True
    

    当然,python中不建议如此检查,更好的办法是使用内置类型检查函数isinstance(obj, cls):

    >>> isinstance(f, Foo)
    True
    

    同样的,内置函数issubclass(cls1, cls2)可以用做子类的检查:

    class SubFoo(Foo):
        pass
    
    >>> issubclass(SubFoo, Foo)
    True
    

    这两个内置函数的第二个参数可以是一个单独的类,也可以是几个类的元组:

    >>> isinstance(1, (str, int))
    True
    

    以下两个特殊方法可用于重新定义这两个内置函数:

    __instancecheck__(cls, obj)

    __subclasscheck__(cls, subcls)

    鸭子类型

    python是门动态语言,调用对象的时候是不用考虑对象的类型的,而只需要对象有某些特定的属性或者行为即可,也就是鸭子类型。

    为了保持程序的松散耦合,我们可以不用继承,而只是模仿一些对象的行为:

    class File(object):
        def open(self):
            print 'open File'
    
    
    class FileLike(object):
        def open(self):
            print 'open FileLike'
    
            
    def foo(f):
        f.open()
    

    只要定义了open()方法的实例,就可以作为函数foo()的参数:

    >>> f = File()
    >>> fl = FileLike()
    >>> foo(f)
    open File
    >>> foo(fl)
    open FileLike
    

    当我们测试对象是否可以作为foo()的参数时,要同时进行两次isinstance()类型检查,如果类型更多的话呢?

    现在我们可以将File和FileLike分组并对其进行一次类型检查:

    class FileLikeClass(object):
        def __init__(self):
            self.reg = set()
            
        def register(self, cls):
            self.reg.add(cls)
    
        def __instancecheck__(self, obj):
            return self.__subclasscheck__(type(obj))
    
        def __subclasscheck__(self, subcls):
            return any(cls in self.reg for cls in subcls.mro())
    
    
    flc = FileLikeClass()
    flc.register(File)
    flc.register(FileLike)
    

    上述代码使用FileLikeClass创造一个对象,将要检查的类型放入对象的一个集合属性中,然后重新定义__instancecheck__(cls, obj)与__subclasscheck__(cls, subcls)方法,

    进行子类检查时,会检查子类及子类的基类是否在集合中,进行类型检查时,会检查对象的类型是否是集合中的类的子类:

    >>> f = File()
    >>> isinstance(f, flc)
    True
    >>> fl = FileLike()
    >>> isinstance(fl, flc)
    True
    

    当然,python提供了一种更正式的机制来分组对象,定义接口并进行类型检查----抽象基类

    抽象基类

    要定义抽象基类,需要使用abc模块,该模块定义一个元类(ABCMeta)和两个装饰器(@abstractmethod,抽象方法)与(@abstractproperty, 抽象特性),使用方法如下:

    from abc import ABCMeta, abstractmethod, abstractproperty
    
    class AbsFile(object):
        __metaclass__ = ABCMeta
    
        @abstractmethod
        def open(self):
            pass
    

    抽象基类就是定义各种方法而不做具体实现的类,抽象基类是无法实例化的:

    >>> AbsFile()
    TypeError: Can't instantiate abstract class AbsFile with abstract methods open

    任何继承自抽象基类的类也必须实现这些方法,否则无法实例化:

    class File(AbsFile):
        pass
    

    实例化失败:

    >>> File()
    TypeError: Can't instantiate abstract class File with abstract methods open
    

    需定义open方法:

    class File(AbsFile):
        def open(self):
            print 'open File'
    

    当然抽象基类中也可以对抽象方法进行具体实现,在子类中可以通过super()来调用:

    class AbsFile(object):
        __metaclass__ = ABCMeta
    
        @abstractmethod
        def open(self):
            print 'open AbsFile'
        
    
    class File(AbsFile):
        def open(self):
            super(File, self).open()
            print 'open File'
    

    实例化:

    >>> File().open()
    open AbsFile
    open File

    如果只想执行类型检查的话,可以使用register()方法:

    from abc import ABCMeta, abstractmethod, abstractproperty
    
    class AbsFile(object):
        __metaclass__ = ABCMeta
    
        @abstractmethod
        def open(self):
            pass
        
    
    class File(object):
        pass
    
    AbsFile.register(File)
    

    向抽象基类注册某个类时,是不会检查该类是否实现了抽象基类的抽象方法或者特性的,只会影响类型检查:

    >>> f = File()
    >>> isinstance(f, AbsFile)
    True
    

    python的collections模块定义了与序列,集合和字典有关的各种抽象基类,numbers模块定义了与数字有关的抽象基类。

  • 相关阅读:
    谷歌阅读器将于2013年7月1日停止服务,博客订阅转移到邮箱
    SelfIntroduction
    WCF(四) Configuration file (配置文件)
    亚当与夏娃的礼物
    WCF(三) Message pattern
    面试小题
    分内分外
    C#多线程处理之AutoResetEvent和ManualResetEvent
    WCF(五) Host WCF Service
    ARX中的Purge
  • 原文地址:https://www.cnblogs.com/linxiyue/p/8025871.html
Copyright © 2020-2023  润新知