美文网首页
Python类-magic methods魔术方法, since

Python类-magic methods魔术方法, since

作者: Mc杰夫 | 来源:发表于2022-04-19 16:25 被阅读0次

    (2022.04.19-20 Tues-Wed)
    Python中的魔术方法,是可以定义在类中的特殊方法,可在特定的情况下调用。魔术方法以双下划线(underscore)包裹,形式如__call__, __new__等。本文整理常用的magic method和对应的功能,并给出实例。

    magic methods列表

    magic method的功能大致分为如下几类:

    • 构造与初始化
    • 运算符
    • 类型转换
    • 类表达
    • 控制属性access
    • 调用(callable objects)
    • 赋值
    magic method description
    __init__(self, [...) 初始化器,接收参数
    __new__(cls, [...) 构造函数-创建类、实例化、返回类的实例
    __del__(self) 析构函数-对象进行垃圾收回时调用
    __cmp__(self, other) 比较运算符,分别返回1,0,-1对于self>other, self=other和self=other的情况
    __eq__(self, other) == equality operator的行为
    __ne__(self, other) !=
    __lt__(self, other) <
    __gt__(self, other) >
    __le__(self, other) <=
    __ge__(self, other) >=
    __pos__(self) +some_object
    __ne__(self) -some_object
    __abs__(self) abs()
    __invert__(self) invert运算符,反转
    __round__(self) round()
    __floor__(self) math.floor()
    __ceil__(self) math.ceil()
    __trunc__(self) math.trunc()
    __add/radd__(self, other) 加法/反加操作 reflected addition
    __sub/rsub__(self, other) 减法/反减
    __mul/rmul__(self, other) 乘/反乘
    __floordiv/rfloordiv__(self, other) //
    __div/rdiv__(self, other) /
    __mod/rmod__(self, other) %
    __pow/rpow__ **
    __and/or/xor__(self, other) &/ | / ^
    __iadd__(self, other) 自加,其他运算符以此类推
    __int__(self) 类型转换为int,其他的包括float, long, complex, oct, hex
    __str__(self) 返回str,当前实例文本显示的内容,即print(this_ins),针对人显示
    __repr__(self) 直接调用当前实例的显示内容,针对机器显示,>> this_ins返回的内容
    __class__ 查看当前实例所属的类,不传递参数
    __format__(self, formatstr) "Hello, {0:abc}!".format(a) would lead to the call a.__format__("abc")
    __getattr__(self, attr) 定义访问不存在的属性attr的返回结果
    __getattribute__(self, attr) 定义访问一个存在的属性attr的返回结果
    __setattr__(self, attr, value) 定义设定一个属性attr的值成为value的时候的操作
    __delattr__(self, attr) 定义执行del attr的操作
    __call__(self, para) 像调用函数一样调用一个实例
    __getitem__(self, key) 用key作为index访问的返回值,self[key]
    __setitem__(self, key, value) 设定index为key的元素值为value,self[key] = value
    __delitem__(self, key) 删除index为key的元素
    __annotations__ 查看函数的注释

    其他magic method包括可迭代对象的__iter__, __next__,上下文管理的__enter__, __exit__,类注释的__doc____slot__, __dict__,定义len()函数的方法__len__等。

    说明和举例:

    • __init____new__的差别:
      这两个方法都在类的实例化时被创建。__new__被首先调用,__init__其次。__new__返回新的类实例,__init__为新的实例设置属性,无返回值。
    class A(object): 
        def __new__(cls): 
             print("New method") 
             return super(A, cls).__new__(cls) 
       
        def __init__(self): 
            print("Init method ") 
    
    >>> A() 
    New method
    Init method 
    <__main__.A object at 0x1016a2b80>
    

    (2022.04.28 Thu)
    __new__覆盖初始类的__new__方法,用于实现工厂模式、单例模式、元编程(需要用__new__控制对象创建)。下面给出工厂模式的一个例子。

    class Shape(object):
        def __init__(self):
            pass
        def draw(self):
            pass
    
    class Rectangle(Shape):
        def __init__(self):
            print('This is a rectangle.')
        def draw(self):
            print('drawing a rectangle')
    
    class Square(Shape):
        def __init__(self):
            print('This is a square.')
        def draw(self):
            print('drawing a square.')
    
    class ShapeFactory(object):
        shapes = {'rectangle': Rectangle, 'square': Square}
        def __new__(cls, name):
            if name in ShapeFactory.shapes.keys():
                print('creating a new shape: {}'.format(name))
                return ShapeFactory.shapes[name]() #返回实例化的对象
            else:
                print('creating a shape')
                return Shape()
    

    调用类ShapeFactory

    >>> a = ShapeFactory('rectangle') # 这里的a是已经经过实例化的对象,不需要再加括号调用
    creating a new shape: rectangle
    This is a rectangle.
    >>> a.draw()
    drawing a rectangle
    
    • __init__()__init__.py的区别
      (2022.11.04 Fri)
      上面已经介绍到__init__方法用于为新的类实例设置属性,而一个 文件夹中的__init__.py文件则有完全不同的作用。

    Python的模块(Module)指的是一个单独的Python文件,而包(package)指的是一个文件夹,该文件夹是模块的集合。

    当用户使用import指令从一个文件夹引入module/package时,如果该文件夹本身含有__init__.py文件,则从该文件夹的目录下引入文件时,编译器将该文件夹当做package对待。

    __init__.py文件用于将Python的包package初始化。

    通常__init__.py文件为空,但可以为它增加功能。在导入一个包时,实际上是导入了该包所在文件夹内的__init__.py文件。这样我们可以在__init__.py文件中批量导入所需模块,而不再需要逐一导入。

    当使用import指令因为文件夹时,会首先执行该文件夹下的__init__.py文件。如果该文件中也有导入命令,则执行。当用户使用from xxx import *这个指令时,会导入该文件夹内的所有module。如果控制该命令导出的内容,可在__init__.py文件中加入__all__=[xxx]的命令,用以限制*导出的方法。具体内容参考本文的__all__部分。

    • __getattr__, __getattribute__的差别
      __getattr__用于定义查询不存在的属性时的操作,而__getattribute__定义了查询存在的属性时的操作。默认情况下访问不存在的属性返回AttributeError: xx object has no attribute xx.
      注意当访问属性时,首先调用__getattribute__方法,如果访问的属性在实例中不存在,则再调用__getattr__方法。如下面案例所示
    class attrc:
        var = 0
        def __getattr__(self, attr):
            print('get a non-existing attribute')
            return '1'
        def __getattribute__(self, attr):
            print('get an existing attribute')
            return super().__getattribute__(attr)
    

    调用

    >>> m = attrc()
    >>> m.var 
    get an existing attribute
    0
    >>> m.k #m中不含k这个属性
    get an existing attribute
    get a non-existing attribute
    '1'
    
    • __call__
    class cs:
        def __call__(self, val=None):
            if val:
                print('value = ', val)
            return 'call return: 1'
    

    调用实例

    >>> m = cs()
    >>> m()
    'call return: 1'
    >>> m(99)
    value =  99
    'call return: 1'
    
    • __add__和继承list的案例
      设计一个新的数据类型,特征和list完全一致,除了加法操作由list的拼接改成对应元素相加。
    class listLike(list):
        def __add__(self, al):
            if isinstance(al, list) or isinstance(al, tuple):
                pass
            else:
                return 'TypeError: add a list or tuple'
            if len(al) < len(self):
                return 'LengthError: specify a longer sequence'
            return [self[i]+al[i] for i in range(len(self))]
    
        def __radd__(self, al):
            return self.__add__(al)
    

    调用

    >>> a = listLike([1,2,3])
    >>> a+[4,5,6]
    [5, 7, 9]
    >>> a
    [1, 2, 3]
    >>> [4,5,6]+ a
    [5, 7, 9]
    
    • __annotations__查看函数注释
      (2022.05.27 Fri)
      Python 3.0加入了函数注释功能,比如下面例子中的func函数,输入的两个参数分别是a1a2类型分别是strint,默认值为1,函数返回类型为int。注意赋默认值的变量放在后面。
    def func(a1:str, a2:int=1) ->int:
        """
        this is function help info
        """
        return 1
    

    此时查看该函数的内置magic method/method decorated by @property __annotations__,会看到返回的字典其中包含了函数的输入输出类型。

    >> func.__annotations__
    {'a1': <class 'str'>, 'a2': <class 'int'>, 'return': <class 'int'>}
    

    另外,可以通过help函数查看该函数在定义之后的解释信息

    >> help(func)
    Help on function func in module __main__:
    
    func(a1: str, a2: int = 1) -> int
        this is function help info
    

    查看help信息结束,按q退出。

    (2022.11.02 Wed)

    • __all__
      当使用importfrom指令(from xxx import *)从模块中导入方法/子模块时,导入的属性、方法、子类的特点是不以单下划线_开头的对象。

    比如如下的Python文件

    # test__all__.py
    
    def func1():
        print(1)
    
    def func2():
        print(2)
    
    def _func3():
        print(3)
    

    导入之后,查看内存中的对象

    >> from test__all__ import *
    >> dir()
    ['__annotations__', '__builtins__', '__doc__', '__loader__', 
    '__name__', '__package__', '__spec__', 'func1', 'func2']
    

    没有下划线开头的对象func1func2被导入内存,以下划线开头的对象_func3没有被导入内存。

    除此之外,可在test__all__模块中加入__all__变量,其值为列表,存储的是当前模块成员(属性、方法、子类)的名称。在模块中设置了__all__变量,当代码中通过from xxx import *导入模块时,只有__all__变量中指定的成员会被导入,即便该成员的名字是以下划线_开头。

    # test__all__.py
    
    def func1():
        print(1)
    
    def func2():
        print(2)
    
    def _func3():
        print(3)
    
    __all__ = ['func1', '_func3']
    

    导入

    >> from test__all__ import *
    >> dir()
    ['__annotations__', '__builtins__', '__doc__', '__loader__', 
    '__name__', '__package__', '__spec__', '_func3', 'func1']
    

    变量__all__的设置是针对from xxx import *的命令,一旦采用其他方式导入,__all__的设置将失效,分别是import xxxfrom xxx import func2, _func3

    >> import test__all__ as ta
    >> ta.func2()
    2
    >> from test__all__ import func2, _func3
    >> func2()
    2
    >> _func3()
    3
    

    Reference

    1 rszalski点github点io/magicmethods/

    相关文章

      网友评论

          本文标题:Python类-magic methods魔术方法, since

          本文链接:https://www.haomeiwen.com/subject/gjpsertx.html