魔术方法

[TOC]

魔术方法

  • "魔术方法"指被用于重载Python的操作符或内置方法

  • 每个魔术方法都有特定的目的, 当特定语法出现时, 它作为执行的钩子(钩子是指在特定事件发生时, 能够为响应事件而调用的代码或函数, 回调函数算是钩子的一种)

  • 魔术方法需要特定的函数名称和签名, 然后会在特定情况下被调用

创建与销毁

___init__方法

  • 在创建实例后会立即执行对象的__init__方法

  • 该方法必须接受一个位置参数(self), 然后可以接受任意数量的可选参数

  • 该方法并没有创建新对象(该操作由__new__完成), 只在为创建后的对象提供初始化数据

  • __init__方法并不返回任何值, 如果使用return语句返回值则会导致TypeError错误

    import sys
    print("Python版本信息: ", sys.version)
    
    class Person(object):
        def __init__(self, name='name'):
            self.name = name
            return name
        pass
    p = Person()

    输出

    D:\Software\Installed\Python\Python36\python.exe D:/workspace/Pycharm/PurePythonProject/Test07.py
    Python版本信息:  3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
    Traceback (most recent call last):
      File "D:/workspace/Pycharm/PurePythonProject/Test07.py", line 13, in <module>
        p = Person()
    TypeError: __init__() should return None, not 'str'
    
    Process finished with exit code 1

__new__方法

  • __new__方法在__init__之前执行, 用于创建类的实例, __new__方法才是实际上创建并返回实例的方法

  • __new__永远是静态的

  • 简单实例

    import sys
    
    print("Python版本信息: ", sys.version)
    
    class Person(object):
        def __new__(cls, *args, **kwargs):
            instance = super(Person, cls).__new__(cls)
            print('Person.__new__(A): ', kwargs)
            kwargs['name'] = 'Snow'
            kwargs['age'] = '20'
            print('Person.__new__(B): ', kwargs)
            instance.name = 'Go'
            return instance
            pass
    
        def __init__(self, *args, **kwargs):
            self.name = kwargs.get('name')
            print('Person.__init__: ', kwargs)
            pass
        pass
    class Teacher(Person):
        def __new__(cls, *args, **kwargs):
            instance = super(Teacher, cls).__new__(cls, *args, **kwargs)
            kwargs['salary'] = '3000'
            print('Teacher.__new__: ', kwargs)
            instance.name = 'Join'
            return instance
            pass
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.salary = kwargs.get('salary')
            print('Teacher.__init__: ', kwargs)
            pass
    
    p = Person(name='Hodor')
    print(p.name)
    
    print('\n', '*' * 50, '\n')
    
    t = Teacher(name='Hodor', salary='5000')
    print(t.name, t.salary)

    输出

    D:\Software\Installed\Python\Python36\python.exe D:/workspace/Pycharm/PurePythonProject/Test07.py
    Python版本信息:  3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
    Person.__new__(A):  {'name': 'Hodor'}
    Person.__new__(B):  {'name': 'Snow', 'age': '20'}
    Person.__init__:  {'name': 'Hodor'}
    Hodor
    
     ************************************************** 
    
    Person.__new__(A):  {'name': 'Hodor', 'salary': '5000'}
    Person.__new__(B):  {'name': 'Snow', 'salary': '5000', 'age': '20'}
    Teacher.__new__:  {'name': 'Hodor', 'salary': '3000'}
    Person.__init__:  {'name': 'Hodor', 'salary': '5000'}
    Teacher.__init__:  {'name': 'Hodor', 'salary': '5000'}
    Hodor 5000
    
    Process finished with exit code 0
  • 大多数情况下, __new__方法的其他参数会被完整复制到__init__方法中

  • 参数在调用构造函数时首先会被传递给__new__方法(由于先被调用)

  • 从上例可以看出, 参数在__new__方法中无法被修改

__del__方法

  • __del__方法在对象被销毁时调用

  • 无论是直接删除或是由垃圾回收器进行销毁, 都会调用__del__方法

类型转换

Python中有很多用于将复杂对象转换为简单对象或常用数据类型的魔术方法.

__str__

该方法接受一个位置参数self, 并在对象被传递给str的构造函数时被调用, 然后返回一个字符串.

deepin@ThinkCentre:~/Desktop$ python3
Python 3.5.2+ (default, Nov  3 2016, 11:10:16) 
[GCC 6.2.0 20161027] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> class MyObject(object):
...     def __str__(self):
...             return "My Awesome Object!"
... 
>>> str(MyObject())
'My Awesome Object!'
  • Python2中有__unicode__方法, 该方法在对象传递给unicode的构造函数时被调用

  • Python3中有__bytes__方法, 该方法在对象传递给bytes的构造方法时被调用

__bool__

用来界定对象TrueFalse.该类型的操作, 在Python3中由__bool__魔术方法处理, 在Python2中由__nonzero__魔术方法处理.

__int__, __float__, __complex__

某些情况下, 将复杂对象转换为基本类型的数字十分有价值. 如果一个对象定义了一个返回INT类型的__int__方法, 那么该对象被传递给int的构造函数时, __int__方法会被调用.

比较

对象在进行相等性测试(==!=)或不等性测试(<, <=等)时进行比较, 这些操作符与Python中的魔术方法一一对应.

二元相等性

__eq__

  • __eq__方法在对象使用==操作符进行比较时被调用

  • 优先调用左侧对象__eq__方法

  • 要确保相等性检测始终可交换

  • 如果被比较的两个对象中一个对象是另一个对象的直接子类, 将使用子类的__eq__方法

__ne__

  • __ne__在使用!=操作符时被调用

  • 如果没有定义__ne__方法, 那么Python会调用__eq__方法, 并对结果取反

相对比较

  • __lt__: <

  • __le__: <=

  • __gt__: >

  • __ge__: >=

  • 通常只需定义__eq____lt__(或__gt__), 那么所有6个比较操作都可以正常执行

__cmp__

  • __cmp__方法是为对象定义相对比较的旧有(不推荐)方式

  • 接受selfother两个参数, 如果self比other小, 返回负数; self比other大返回正整数; 相等时返回0

  • __cmp__在python2中被淘汰; 在python3中不可用

操作符重载

二元操作符

Python为每个操作符提供了三种重载方式.

  • 普通方法(vanilla method): 表达式 x+yx.__add__(y)匹配

  • 取反方法(reversemethod): 只有在第一个操作对象不提供对应方法并且操作对象类型不同(或返回NotImplemented)时才调用取反(操作符两边对象顺序交换). 定义该类魔法方法是在方法名称的开头加上r. 因此对于表达式x+y, 如果x没有定义__add__方法, 则调用y.__radd__(x)

  • 即席方法(in-place method): 此类操作符包括+=, *=等, 定义此类方法, 只需在普通方法名加上i. 因此表达式x+=y将会调用x.__iadd__(y)

操作符

方法

取反

即席

+

__add__

__radd__

__iadd__

-

__sub__

__rsub__

__isub__

*

__mul__

__rmul__

__imul__

/

__truediv__

__rtruediv__

__itruediv__

//

__floordiv__

__rfloordiv__

__ifloordiv__

%

__mod__

__rmod__

__imod__

**

__pow__

__rpow__

__ipow__

&

__and__

__rand__

__iand__

`

`

__or__

__ror__

__ior__

^

__xor__

__rxor__

__ixor__

<<

__lshift__

__rlshift__

__ilshift__

>>

__rshift__

__rrshift__

__irshift__

一元操作符

  • +(正): __pos__

  • -(负): __neg__

  • ~(取反): __invert__

重载常见方法

  • __len__: 长度

  • __repr__: 用来确定该对象在Python交互式终端中的显示方式

    ```python

    代码

    class TimeSpan(): def init(self, hours=0, minutes=0, seconds=0): self.hours = hours self.minutes = minutes self.seconds = seconds

    def __repr__(self):
        return 'TimeSpan(hours=%d, minutes=%d, seconds=%d)' % (self.hours, self.minutes, self.seconds)

class TimeSpanWithoutRepr(): def init(self, hours=0, minutes=0, seconds=0): self.hours = hours self.minutes = minutes self.seconds = seconds

print(TimeSpan()) print(TimeSpanWithoutRepr())

输出

TimeSpan(hours=0, minutes=0, seconds=0)

```

  • __hash__: 通过数字化表达式唯一标识对象.

    • 当一个对象传递给散列函数时, 调用其__hash__方法.

    • 该方法接受一个位置参数, 返回一个整形值, 该值可为负数.

    • 如果一个对象定义了__eq__方法, 则__hash__方法隐式地被赋值为None.

    • 常见用法: 用于字典的键值与set对象. 仅有可哈希化的对象可以最为字典的键值; 仅有可哈希的对象可以在set对象中存在.

  • __format__: 该函数根据Python的格式化规范来格式化不同类型的对象.

最后更新于

这有帮助吗?