heidern0612 发表于 2019-1-9 10:16:23

【043讲心得】【python的魔法方法总结】

本帖最后由 heidern0612 于 2019-1-9 10:53 编辑

写心得的过程都是自我思考的过程,借鉴了很多论坛前辈和互联网大佬的经验,仓促间难免有所疏漏,如果错误,恳请指出,不胜感激。


python的魔法方法很奇特,有些地方也称他为特殊方法。

其结构是由两个下划线(“_”开始中间名称最后以两个下划线(“_”)结束的特殊指定方法,是python中定义的一种规则,一种玩法。

python中的多态是基于动作和特性的实现的,即操作动作决定方法实现,只要对象实现了对应的魔法方法就可使用python中的某些特殊操作方式对该对象进行操作,而一般这种”特殊操作“的本质是将其内到python的内建函数的操作中。

而内建函数操作的本质是根据区分操作对象的数据结构进行调用的,所以魔法方式的作用是通过重写类的魔法方法从而实现将类转化为某类数据结构类型的作用(常见的结构类型有:序列、映射、集合和特殊的结构类型迭代器等)。

所以魔法方法的大部分内容是围绕如下几个部分的(类,方法,重写,内建函数,数据结构)来展开的。

可以分为如上述目录等几个部分进行分段了解:






1、内建函数

python内建函数是一个比较重要的功能,内建函数在某些特定的情况下可以增强代码的复杂性和可读性。

内建函数是存在于python的’* builtins ‘模块的一些函数,相对于其他模块,存在于’ __buildins__‘模块的函数与变量并不需要我们通过import导入,属于python内置模块。

可通过dir(builtins)来获取* builtins 中的对象和函数:


#请自行去掉#号测试:

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

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

#classmethodhelp doc
#help(classmethod)   

#staticmethod help doc
#help(staticmethod)

#iter help doc
#help(iter)

help(next)

内置函数中有dir可以获取python中的模块信息,还有类似abs之类的数学计算的函数。

如下,仅仅介绍几个魔法方法以及和类相关的内置函数:


A、classmethod(function)

将方法function封装成类的方法,被定义成装饰器(decorator)。

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

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

    def classB(cls):
      print ("类方法B")

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


B、staticmethod

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

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

    def methodB():
      print("静态方法B")
      
    methodB=classmethod(methodB) #使用方法调用.



C、iter(object[, sentinel)]

Python 3中关于iter(object[, sentinel)]方法有两个参数,使用iter(object)这种形式比较常见。

iter(object, sentinel)这种形式一般较少使用,Python的文档说明貌似也不容易理解。

故,在此举例说明一下此函数的用法。

①、iter(object)

Python官方文档对于这种形式的解释很容易理解。

此时,object必须是集合对象,且支持迭代协议(iteration protocol)或者支持序列协议(sequence protocol)。

说白了,也就是实现了__iter__()方法或者__getitem__()方法。

list1 =
for i in iter(list1):
    print(i)

②、iter(object, sentinel)

Python官方文档对于这种形式的解释是:“ If the second argument, sentinel, is given, then object must be a callable object.

The iterator created in this case will call object 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.”。

这句话的意思是说:

如果传递了第二个参数,则object必须是一个可调用的对象(如函数)。

此时,iter创建了一个迭代器对象,每次调用这个迭代器对象的__next__()方法时,都会调用object。

如果__next__的返回值等于sentinel,则抛出StopIteration异常,否则返回下一个值。

class counter:

    def __init__(self, _start, _end):
      self.start = _start
      self.end = _end

    def get_next(self):
      s = self.start
      if(self.start < self.end):
            self.start += 1
      else:
            raise StopIteration
      return s


c = counter(1, 5)
iterator = iter(c.get_next, 3)
print(type(iterator))

for i in iterator:
   print(i)


D、len(s)

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


E、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.

自行gogle翻译:戳我前进。




2、类的方法:

类的方法分为类的创建方法(_new_),类的初始化方法(_init_),类的方法(classmethod),类的静态方法(staticmethod),类的方法,类的销毁方法(_del_),和类的魔法方法(之后介绍)

如下通过一个示例介绍各个类型方法:

class Base(object):
    '基本类型举例'

    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 ("类方法")


    @staticmethod
    def B():
      print ("静态方法")


    def C(self):
      print ("实例自身")

    def __str__(self):
      return "内置方法str返回"

    def __del__(self):
      print ("调用__del__")


if __name__== "__main__":
    a = Base()
    print (str(a))
   
    try:
      print ("实例调用")
      #call the class method
      a.A()
      #call static method
      a.B()
      #call the instance method
      a.C()

      print ("类调用")
      # call the classmethod
      Base.A()
      # call static method
      Base.B()
      #call instance method
      Base.C(Base())
      #call instance method
      Base.C()
    except:
      pass

    del(a)



①、_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()) * 。






3、类的属性

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

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)


结果如下图:




①、类的属性

如上,类的属性也可以认为是静态属性,可以直接通过类来调用,CLASS_NAME和instanceCount就是这样的属性.



②、实例属性

实例属性是归属于实例对象本身的,需要直接使用实例对象来调用,如上的self.name和self.value。



③、“私有属性“

python中并不存在只能从内部访问的私有变量类型,但python代码都遵守的公约规定:带有下划线(‘_‘)前缀开头的变量或方法被示为非公开API的一部分。

其有两种情况,一种是前面至少有两个下划线(‘__‘)开头,后面至多有一个下划线(‘_‘)结束。

python对其使用name mangling机制做了一个简单的支持,会将其标识符替换成_className__name,如上的__instance 。

另一种是以一个”“开始的变量在被作为一个module导入时,使用 from module import * 后并不可以直接使用其直接使用,还是需要通过类或者实例变量配合使用。



④、魔法属性

python中的魔法属性是对类或实例的一些数据的描述整合。

如上的”_dict_“位一个字典映射对象,存储一个类或者实例的可读属性。

类似的魔法属性还包括:_class__*、**__bases_、_name_、_mro_、_subclasses_等。




4、魔法方法的使用

如上,已经说了不少有关魔法方法的使用。

如_new_、_init_、_del_等特定的类生命周期相关的魔法方法,也有功能性的如_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


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

    #当尝试给一个属性赋值时调用
    def __setattr__(self, name, value):
      self.__dict__ = 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这样的切片操作,一般针对于序列对象
    def __getitem__(self, key):
      return self.__dict__

    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

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



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



返回值如下:

base __init__
True
1
10

Base


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


①、普通关联回调内建函数

这种比较简单,在类中重写对应的魔法方法,使用对应的回调返回重写方法的返回值,大多的魔法方法都是这类的,如上_len_、_str_、_unicode_等就属于这类。


②、属性相关魔法方法

与属性相关的魔法方法_getattr_、_setattrr_、_getitem_、__setitem_、_getattribute_,

其中_getitem_、__setitem_属于序列或映射的魔法方法,而_getattr_、_setattrr_属于获取类中变量属性的设置,用于获取调用情况。

其中_getattr_ 属于当类中无法获取该变量时调用,而_getattribute_会拦截所有属性的获取。


③、描述器

对于实现了_get_、_set_、_del_魔法方法的类可以称之为描述器。

同时具有_get_和_set_方法,这样的descriptor叫做data descriptor,如果只有_get_方法,则叫做non-data descriptor。


④、迭代器

python中的迭代器是实现了_iter_魔法方法的类,并通过实现 next(self)方法进行迭代调用,如上示例。



⑤、生成器

python中有一个特别的迭代器,可以通过关键词yield进行迭代返回,如下格式:

def repeater(value):
    while True:
      new = (yield value)
      if new is not None:
            value=new


r = repeater(42)

print(next(r))
print(r.send("Hello world!"))





5、Python 魔法方法表格大全



魔法方法含义

基本的魔法方法
__new__(cls[, ...])1. __new__ 是在一个对象实例化的时候所调用的第一个方法
2. 它的第一个参数是这个类,其他的参数是用来直接传递给 __init__ 方法
3. __new__ 决定是否要使用该 __init__ 方法,因为 __new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 __new__ 没有返回实例对象,则 __init__ 不会被调用
4. __new__ 主要是用于继承一个不可变的类型比如一个 tuple 或者 string
__init__(self[, ...])构造器,当一个实例被创建的时候调用的初始化方法
__del__(self)析构器,当一个实例被销毁的时候调用的方法
__call__(self[, args...])允许一个类的实例像函数一样被调用:x(a, b) 调用 x.__call__(a, b)
__len__(self)定义当被 len() 调用时的行为
__repr__(self)定义当被 repr() 调用时的行为
__str__(self)定义当被 str() 调用时的行为
__bytes__(self)定义当被 bytes() 调用时的行为
__hash__(self)定义当被 hash() 调用时的行为
__bool__(self)定义当被 bool() 调用时的行为,应该返回 True 或 False
__format__(self, format_spec)定义当被 format() 调用时的行为
有关属性
__getattr__(self, name)定义当用户试图获取一个不存在的属性时的行为
__getattribute__(self, name)定义当该类的属性被访问时的行为
__setattr__(self, name, value)定义当一个属性被设置时的行为
__delattr__(self, name)定义当一个属性被删除时的行为
__dir__(self)定义当 dir() 被调用时的行为
__get__(self, instance, owner)定义当描述符的值被取得时的行为
__set__(self, instance, value)定义当描述符的值被改变时的行为
__delete__(self, instance)定义当描述符的值被删除时的行为
比较操作符
__lt__(self, other)定义小于号的行为:x < y 调用 x.__lt__(y)
__le__(self, other)定义小于等于号的行为:x <= y 调用 x.__le__(y)
__eq__(self, other)定义等于号的行为:x == y 调用 x.__eq__(y)
__ne__(self, other)定义不等号的行为:x != y 调用 x.__ne__(y)
__gt__(self, other)定义大于号的行为:x > y 调用 x.__gt__(y)
__ge__(self, other)定义大于等于号的行为:x >= y 调用 x.__ge__(y)
算数运算符
__add__(self, other)定义加法的行为:+
__sub__(self, other)定义减法的行为:-
__mul__(self, other)定义乘法的行为:*
__truediv__(self, other)定义真除法的行为:/
__floordiv__(self, other)定义整数除法的行为://
__mod__(self, other)定义取模算法的行为:%
__divmod__(self, other)定义当被 divmod() 调用时的行为
__pow__(self, other[, modulo])定义当被 power() 调用或 ** 运算时的行为
__lshift__(self, other)定义按位左移位的行为:<<
__rshift__(self, other)定义按位右移位的行为:>>
__and__(self, other)定义按位与操作的行为:&
__xor__(self, other)定义按位异或操作的行为:^
__or__(self, other)定义按位或操作的行为:|
反运算
__radd__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rsub__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rmul__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rtruediv__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rfloordiv__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rmod__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rdivmod__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rpow__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rlshift__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rrshift__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__rxor__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
__ror__(self, other)(与上方相同,当左操作数不支持相应的操作时被调用)
增量赋值运算
__iadd__(self, other)定义赋值加法的行为:+=
__isub__(self, other)定义赋值减法的行为:-=
__imul__(self, other)定义赋值乘法的行为:*=
__itruediv__(self, other)定义赋值真除法的行为:/=
__ifloordiv__(self, other)定义赋值整数除法的行为://=
__imod__(self, other)定义赋值取模算法的行为:%=
__ipow__(self, other[, modulo])定义赋值幂运算的行为:**=
__ilshift__(self, other)定义赋值按位左移位的行为:<<=
__irshift__(self, other)定义赋值按位右移位的行为:>>=
__iand__(self, other)定义赋值按位与操作的行为:&=
__ixor__(self, other)定义赋值按位异或操作的行为:^=
__ior__(self, other)定义赋值按位或操作的行为:|=
一元操作符
__neg__(self)定义正号的行为:+x
__pos__(self)定义负号的行为:-x
__abs__(self)定义当被 abs() 调用时的行为
__invert__(self)定义按位求反的行为:~x
类型转换
__complex__(self)定义当被 complex() 调用时的行为(需要返回恰当的值)
__int__(self)定义当被 int() 调用时的行为(需要返回恰当的值)
__float__(self)定义当被 float() 调用时的行为(需要返回恰当的值)
__round__(self[, n])定义当被 round() 调用时的行为(需要返回恰当的值)
__index__(self)1. 当对象是被应用在切片表达式中时,实现整形强制转换
2. 如果你定义了一个可能在切片时用到的定制的数值型,你应该定义 __index__
3. 如果 __index__ 被定义,则 __int__ 也需要被定义,且返回相同的值
上下文管理(with 语句)
__enter__(self)1. 定义当使用 with 语句时的初始化行为
2. __enter__ 的返回值被 with 语句的目标或者 as 后的名字绑定
__exit__(self, exc_type, exc_value, traceback)1. 定义当一个代码块被执行或者终止后上下文管理器应该做什么
2. 一般被用来处理异常,清除工作或者做一些代码块执行完毕之后的日常工作
容器类型
__len__(self)定义当被 len() 调用时的行为(返回容器中元素的个数)
__getitem__(self, key)定义获取容器中指定元素的行为,相当于 self
__setitem__(self, key, value)定义设置容器中指定元素的行为,相当于 self = value
__delitem__(self, key)定义删除容器中指定元素的行为,相当于 del self
__iter__(self)定义当迭代容器中的元素的行为
__reversed__(self)定义当被 reversed() 调用时的行为
__contains__(self, item)定义当使用成员测试运算符(in 或 not in)时的行为






参考文献如下:

1、Python基础教程-魔法方法、属性和迭代器

2、Python 在线官方文档

3、部分CSDN大佬的文章

4、部分博客园大佬的文章

5、度娘


   感谢互联网,感谢鱼C论坛,可以让我踩在巨人的肩膀上一步步努力前行。

启明星辰 发表于 2020-6-5 05:19:07

我发现了宝藏!
页: [1]
查看完整版本: 【043讲心得】【python的魔法方法总结】