胖蔡说技术
随便扯扯

Python 魔法方法与属性

python的魔法方法很奇特,有些地方也称他为特殊方法。其结构是由两个下划线(“”开始中间名称最后以两个下划线(“”)结束的特殊指定方法,这有点类似java中的接口和abstract,是python中定义的一种规则,一种玩法。这也是python与java对于方法重写的一个较大的不同的地方.Java中我们可能会为了实现某种或某类相似的动作行为(action),从而使用接口式编程,抽象方法来抽象实现过程从而实现其多态性
,而且子类(非抽象类)继承了抽象父亲或者实现了接口后,必须要是实现所继承父类或接口的抽象方法,可以明显发现Java中的多态是基于对象的(父类或接口),而python中的“接口实现方式”方式与其有很大的不同,python中的多态是基于动作和特性的实现的,即操作动作决定方法实现,只要对象实现了对应的魔法方法就可使用python中的某些特殊操作方式对该对象进行操作,而一般这种”特殊操作“的本质是将其内到python的内建函数的操作中。而内建函数操作的本质是根据区分操作对象的数据结构进行调用的,所以木法方式的作用是通过重写类的魔法方法从而实现将类转化为某类数据结构类型的作用(常见的结构类型有:序列、映射、集合和特殊的结构类型迭代器等)。所以魔法方法的大部分内容是围绕如下几个部分的(类,方法,重写,内建函数,数据结构)来展开的。可以分为如下几个部分进行分段了解:

  • 内建函数
  • 类的方法
  • 类的属性
  • 魔法方法的使用
  • 参考

内建函数

python内建函数是一个比较重要的功能,内建函数在某些特定的情况下可以代码的复杂性和增强代码的可读性,内建函数是存在于python的’* builtins ‘模块的一些函数,相对于其他模块,存在于’ buildins‘模块的函数与变量并不需要我们通过import导入,属于python内置模块.内建函数的介绍链接Python 内建函数,也可以借助python本省来了解.可以通过dir(builtins)来获取* builtins 中的对象和函数:

print "all params of Built-in :\n"
print dir(__builtins__),"\n"#显示__builtins__模块的所有信息

#print "__builtins__ hep info:\n"
#help(__builtins__) #显示内置函数的基本用法介绍

#classmethod  help doc
#help(classmethod)   
#staticmethod help doc
#help(staticmethod)
#iter help doc
#help(iter)
help(next)
all params of Built-in :

['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__IPYTHON__', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'display', 'divmod', 'dreload', 'enumerate', 'eval', 'execfile', 'file', 'filter', 'float', 'format', 'frozenset', 'get_ipython', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip'] 

Help on built-in function next in module __builtin__:

next(...)
    next(iterator[, default])

    Return the next item from the iterator. If default is given and the iterator
    is exhausted, it is returned instead of raising StopIteration.

内置函数中有dir可以获取python中的模块信息,还有类似abs之类的数学计算的函数。如下,仅仅介绍几个和魔法方法y以及和类相关的内置函数:

  • classmethod(function)

将方法function封装成类的方法,被定义成装饰器(decorator),具体实现可以通过两种方式,一种是直接通过装饰器定义,被装饰器修饰的方法会默认被传入该方法,也可以直接通过方法调用实现,与其类似的有staticmethod函数示例如下:

  class A:

      #装饰器
      @classmethod
      def methodA(cls,args):
           '''method body'''

      def classB(cls):
         print "this is class method"

      #使用方法调用.
      classB=classmethod(classB)
  • staticmethod

用法和classmethod基本一致,如下:

  class A:

      #装饰器
      @staticmethod
      def methodA(args):
           '''method body'''

      def methodB():
         print "this is static method"

      #使用方法调用.
      methodB=classmethod(methodB)
  • iter(source, sentinel=None)

对于iter的介绍,在文档的介绍和python官方文档还是有些区别的,官方doc中的介绍如下:

iter(…)
iter(collection) -> iterator
iter(callable, sentinel) -> iterator

Get an iterator from an object.  In the first form, the argument must
supply its own iterator, or be a sequence.
In the second form, the callable is called until it returns the sentinel.

对于iter中的第一个参数定位为一个iterator or a sequence,但是在官方doc中有不同的介绍:

The first argument is interpreted very differently depending on the presence of the second argument. Without a second argument, o must be a collection object which supports the iteration protocol (the iter() method), or it must support the sequence protocol (the getitem() method with integer arguments starting at 0). If it does not support either of those protocols, TypeError is raised. If the second argument, sentinel, is given, then o must be a callable object. The iterator created in this case will call o with no arguments for each call to its next() method; if the value returned is equal to sentinel, StopIteration will be raised, otherwise the value will be returned.

为此,我进行了一个测试:

class B(object):
    pass

class A():
     def __call__(self, *args, **kwargs):
        print "[call,her,2]"

a1=iter(A(),"a")
print a1.next()
b=B()    
a=iter(b,"2")
print  a.next()
[call,her,2]
None



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-18-6e1b318bdd62> in <module>()
      9 print a1.next()
     10 b=B()
---> 11 a=iter(b,"2")
     12 print  a.next()


TypeError: iter(v, w): v must be callable

如上,可以发现,iter(…)的要求和python官方的doc 要求一致,文档并不能完全介绍完全。当第二参数非空时,需要要求第一参数为实现类迭代功能的对象,如下会介绍(当类实现iter方法),当第二参数存在时,第一个参数必须为可调用对象(实现了call方法)。

  • len(s)

返回一个对象的长度,传入对象可以是list、string、tuple等,也可以是一个类实现了一个魔法方法(len).

  • next(…)

返回一个iterator的下一个元素,传入的对象位一个实现了_next_方法的对象

next(…)
next(iterator[, default])

Return the next item from the iterator. If default is given and the iterator
is exhausted, it is returned instead of raising StopIteration.

类的方法

类的方法分为类的创建方法(_new_),类的初始化方法(_init_),类的方法(classmethod),类的静态方法(staticmethod),类的方法,类的销毁方法(_del_),和类的魔法方法(之后介绍)如下通过一个示例介绍各个类型方法.

class Base(object):
    'a basic class demo'

    def __new__(cls, *args, **kwargs):
        print "__new__"
        return object.__new__(cls,*args, **kwargs)

    def __init__(self):
        print "__init__"



    def __del__(self):
        print "__del__"

    @classmethod
    def A(cls):
        print "class method"


    @staticmethod
    def B():
        print "static method"


    def C(self):
        print "this is self"

    def __str__(self):
        return "build-in str(o) return "

    def __del__(self):
        print "__del__"


if __name__=="__main__":
    a=Base()
    print str(a)
    try:
        print "Begin transfer-----------------------------call by instance."
        #call the class method
        a.A()
        #call static method
        a.B()
        #call the instance method 
        a.C()

        print "Begin transfer------------------------------call by Class"
        # call the class  method
        Base.A()
        # call static method
        Base.B()
        #call instance method
        Base.C(Base())
        #call instance method
        Base.C()
        pring
    except Exception as e:
        print e

    del(a)

__new__
__init__
build-in str(o) return 
Begin transfer-----------------------------call by instance.
class method
static method
this is self
Begin transfer------------------------------call by Class
class method
static method
__new__
__init__
this is self
__del__
unbound method C() must be called with Base instance as first argument (got nothing instead)
__del__
  • _new_ 方法
    _new_ 方法是一个静态方法(同时也是一种魔法方法),用于创建一个类的实例,返回一个类的实例对象用于传入初始化方法_ init_ 中,一般并不需要对其进行声明和重写。若是重写的话,需要注意返回一个有效的类的实例对象,如上实例通过object父类调用类的实例化方法,用于返回一个对象实例。若是不返回一个类的实例对象,会导致类的 _ init_ 方法不会被调用,当然实例(self为空)也不会被成功创建。
  • _init_ 方法
    _init_ 是类创建过程中用的比较多的魔法方法,其是类对象创建后调用的初始化方法,紧跟者new 调用后调用,主要用于实例的变量的初始化操作,和传递类创建传入的变量,init 方法可以理解为一个类的实例的构造器,其方法的特点是不会返回任何对象或者值,若返回则会抛出 TypeError 异常。
  • _del_ 方法
    同样属于魔法方法一种。如上,可以发现当我们调用del(a)来销毁实例对象时,会调用该方法,其该属类的实例的析构函数,del内建函数并不会主动调用del方法,只有当类的实例对象的引用计数为0时才会被调用,要我们主动显示的调用 del 方法完成实例的销毁操作.因此,一般不建议重写del 方法。
  • _str方法
    用于返回对象的string 字段信息,重写该魔法方法的实例可以通过调用内建函数 str(o) 来回调 _str 方法。如上示例有所演示。
  • classmethod 方法
    类的方法,类的方法调用层级归属类级别,其方法定义需要使用 @classmethod 装饰或者调用classmethod内建方法来实现,类的方法的第一个参数默认为类的对象,使用cls 固定表示,如上的 B方法,类的方法和静态方法一样可以通过类的实例对象调用,也可以直接通过类来调用.
  • staticmethod方法
    静态方法,通过装饰器@staticmethod 或者内建函数声明使用,静态方法的方法定义部分python的一般函数定义并无不同,页不需要如同classmethod或实例方法一样传入类的对象或实例的对象,其调用和classmethod类似,可以直接通过类调用或者类的实例对象调用。
  • 实例 方法
    实例方法是归属与创建的实例对象所私有,定义如上def C(self): 其方法需要传入一个实例对象self,且位置为第一个参数,该方法只能由类的实例来调用,不可以通过类直接调用(无参数),若需要通过类直接调用可以通过绑定式的方式将类的实例对象传入该方法中:* Base.C(Base()) * 。

类的属性

类的属性部分主要分为两个小部分进行介绍:归属类或实例对象的自定义属性,归属行为特征所共有定义的魔法属性,给出一个简单的例子,根据案例说明。

class Base(object):
    CLASS_NAME="Base"
    instanceCount=0
    __instance=12
    _aa=1234

    def __init__(self,name,value):
        Base.instanceCount+=1
        self.name=name
        self.value=value

    def __str__(self):
        return "CLASS_NAME:%s,instanceCount:%s,self.name:%s,self.value:%s \n" % (Base.CLASS_NAME,Base.instanceCount,self.name,self.value)


a=Base("a",10)
print "a:[%s}\n" % str(a)
b=Base("b",12)
print "b:[%s}\n"% str(b)

print b.__dict__,"\n"
print Base.__dict__,"\n"
print Base._instance
a:[CLASS_NAME:Base,instanceCount:1,self.name:a,self.value:10 
}

b:[CLASS_NAME:Base,instanceCount:2,self.name:b,self.value:12 
}

{'name': 'b', 'value': 12} 

{'__dict__': <attribute '__dict__' of 'Base' objects>, '__module__': '__main__', 'instanceCount': 2, '_Base__instance': 12, '_aa': 1234, 'CLASS_NAME': 'Base', '__str__': <function __str__ at 0x7f7a142256e0>, '__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None, '__init__': <function __init__ at 0x7f7a14190ed8>} 




---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-26-25ca76d45fc7> in <module>()
     21 print b.__dict__,"\n"
     22 print Base.__dict__,"\n"
---> 23 print Base._instance


AttributeError: type object 'Base' has no attribute '_instance'
  • 类的属性
    如上,类的属性也可以认为是静态属性,可以直接通过类来调用,CLASS_NAME和instanceCount就是这样的属性.
  • 实例属性
    实例属性是归属于实例对象本身的,需要直接使用实例对象来调用,如上的self.name和self.value
  • “私有属性“
    python中并不存在只能从内部访问的私有变量类型,但python代码都遵守的公约规定:带有下划线(‘‘)前缀开头的变量或方法被示为飞公开API的一部分,其有两种情况,一种是前面至少有两个下划线(‘‘)开头,后面之多有一个下划线(‘‘)结束,python对其使用name mangling机制做了一个简单的支持,会将其标识符替换成_className__name,如上__instance ,另一种是以一个”“开始的变量在被作为一个module导入时,使用 from module import * 后并不可以直接使用其直接使用,还是需要通过类或者实例变量配合使用.
  • 魔法属性
    python中的魔法属性是对类或实例的一些数据的描述整合.如上的”dict“位一个字典映射对象,存储一个类或者实例的可读属性。类似的魔法属性还包括:class_、*_basesnamemrosubclasses等。

魔法方法的使用

如上,已经说了不少有关魔法方法的使用,如newinitdel等特定的类生命周期相关的魔法方法,也有功能性的如str等,如下,就是对于魔法方法做一个扩展性的说明。

魔法方法的存在是python的多态性的一种表现,不同其他语言的多态,python的多态更多的表现在动作或者称作功能上,对于想要实现某类功能的类只需要对应实现某些对应的魔法方法,即可通过python的内建函数进行某些特定功能的操作。如常见的求长度、求hash数值、迭代器、生成器的使用。如下,通过一个小的示例来了解一些常用的魔法方法。

class Base(object):
    def __init__(self):
        self.index = 0
        print "base __init__"

    # 返回字符串化
    def __str__(self):
        return "Base"
    #
    # 返回对象长度
    def __len__(self):
        return 10

    # 返回hash数值
    def __hash__(self):
        return 199908

    # 真值判断
    def __nonzero__(self):
        return True

    # 返回转化成一个unicode对象
    def __unicode__(self):
        return u'海飞'

    # 当查找一个一个属性未查找到时调用
    def __getattr(self, name):
        if name == "name":
            return "hfcai"
        else:
            raise Exception("No exists tohis attribute.")

    #当尝试给一个属性赋值时调用
    def __setattr__(self, name, value):
        self.__dict__[name] = value

    # 当属性被删除时调用
    def __delattr__(self, name):
        print "__delattr__:%s" % name

    # 新式类中访问属性的调用,使用超类的方法防止循环调用
    def __getattribute__(self, name):
        return super(Base,self).__getattribute__(name)

    # 新式类作为描述器
    def __get__(self, obj, type=None):
        return 'get', self, obj, type

    def __set__(self, obj, val):
        print 'set', self, obj, val

    def __delete__(self, obj):
        print 'delete', self, obj
    #
    #
    # 接受self[key]这样的切片操作,一般针对于序列对象
    def __getitem__(self, key):
        return self.__dict__[key]

    def __setitem__(self, key, value):
        print "__setitem__"

    def __delitem__(self, key, value):
        print "__delitem__"

    # 迭代器  ,重写表明当前类的示例为一个迭代器
    def __iter__(self):
        return self

    # next
    def next(self):
        self.index+=1
        return self.index

    # 返回一个反向的迭代对象
    def __reversed__(self):
        return [9, 8, 7, 6]

    # 含操作
    def __contains__(self, item):
        return True



a = Base()
#__contain__
print "test" in a
# __iter__
b = iter(a)
print b.next()
# __len__
print len(a)
print reversed(a)
print str(a),unicode(a)

base __init__
True
1
10
[9, 8, 7, 6]
Base 海飞

如上,涉及到的魔法方法较多,分类讲解一部分的魔法方法,其用法类似:

  • 普通关联回调内建函数
    这种比较简单,在类中重写对应的魔法方法,使用对应的回调返回重写方法的返回值,大多的魔法方法都是这类的,如上lenstrunicode等就属于这类。
  • 属性相关魔法方法
    与属性相关的魔法方法getattrsetattrrgetitem_setitemgetattribute,其中getitem_setitem属于序列或映射的魔法方法,而getattrsetattrr属于获取类中的变量的属性的设置获取调用情况。其中getattr 属于当类中无法获取该变量时调用,而getattribute会拦截所有属性的获取。
  • 描述器
    对于实现了getsetdel魔法方法的类可以称之位描述器.同时具有getset方法,这样的descriptor叫做data descriptor,如果只有get方法,则叫做non-data descriptor。
  • 迭代器
    python中的迭代器是实现了iter魔法方法的类,并通过实现 next(self)方法进行迭代调用,如上示例。
  • 生成器
    python中有一个特别的迭代器,可以通过关键词yield进行迭代返回,如下格式:
def reperater(value):
    while True:
        new = (yield value)
        if new is not None:
            value=new


r=repeater(42)
r.next()

42

r.send("Hello world!")

"Hello world!"

如上demo来自《python基础教程 -生成器》一章的示例,其中包含yeild会返回一个对象,并挂起。而在挂起状态可以通过send方法发送一个值,而再次返回发送过的值.

参考

赞(1) 打赏
转载请附上原文出处链接:胖蔡说技术 » Python 魔法方法与属性
分享到: 更多 (0)

请小编喝杯咖啡~

支付宝扫一扫打赏

微信扫一扫打赏