python神秘的魔法函数_python魔法函数

一、参考

二、构造和初始化

2.1 __new__

在对象实例化过程中最先调用的方法是__new__, 该方法接收参数为类,然后将其他参数,传递给__init__, 该魔法函数比较少见,可以使用其,创建单例类; __new__方法是一个类方法,需要携带的第一个参数是类

class T1(object):

_instances = {}

def __new__(class_, *args, **kwargs):

if class_ not in class_._instances:

class_._instances[class_] = super(T1, class_).__new__(class_, *args, **kwargs)

return class_._instances[class_]

def __init__(self, *args, **kwargs):

pass

2.2 __init__

__init__是一个实例方法,用于将构造的实例初始化,在类定义中十分常见

2.3 __del__

类比于C++, __new__和__init__可以当作类的构造函数,__del__充当类的析构函数,该函数在垃圾回收时候调用,而不是在del object时候触发,可以用于添加套接字或者文件的close()逻辑,但是使用需要小心,实际几乎不使用该方法

from os.path import join

class FileObject:

'''Wrapper for file objects to make sure the file gets closed on deletion.'''

def __init__(self, filepath='~', filename='sample.txt'):

# open a file filename in filepath in read and write mode

self.file = open(join(filepath, filename), 'r+')

def __del__(self):

self.file.close()

del self.file

三、重定义运算符

四、获取类表示信息

4.1 __str__

__str__(self)

自定义对类调用str()方法时候的行为

4.2 __repr__

__repr__(self)

定义对类的实例调用repr()时候的行为,str()和repr()的不同之处是:目标受众的不同,repr()的目的是生成主要是机器可读的输出,许多情况下,可能输出为python代码, 而str()一般输出为人类可读的信息

4.3 __unicode__

__unicode__(self)

定义对类实例执行unicode()方法时候的行为,其返回unicode字符串,如果只定义了unicode(),使用str()会报错,所以需要同事定义两个函数

4.4 __format__

__format__(self, formatstr)

定义在新样式字符串格式中使用类实例时候的行为

4.5 __hash__

__hash__(self)

当调用hash()函数时候定义行为,通常用于字典中的key的快速比较是否相同,通常也需要实现__eq__, 遵循下面的规则,

a == b 实际为 hash(a) == hash(b)

4.6 __nonzero__

__nonzero__(self)

定义对类的实例调用bool()时候的行为,返回值为True或者False,取决于具体的类

4.7 __dir__

__dir__(self)

定义对类的实例调用dir()函数时候的行为,返回值是用户的属性列表,通常,实现dir()函数是不必要的,但是,如果重新定义了__getattr__或者__getattribute__或者其他动态生成属性,则其对于交互式使用类非常重要

4.8 __sizeof__

__sizeof__(self)

定义对类实例调用sys.getsizeof()函数时候的行为,返回值为对象的大小(字节为单位),通常对于C扩展实现的Python类有作用

五、控制属性访问

如果与其他语言比较(例如: Java),Python中好像没有真正的封装,例如,没有办法通过公共函数getter和setter定义私有属性,这是事实。

Python通过下列的魔法函数实现属性的封装,而不是使用显式的修饰符

5.1 __getattr__

__getattr__(self, name)

可以定义当用户试图访问一个不存在的属性时候的行为,这对于捕获或者重定向常见的拼写错误、引发警告等非常有用,只有当访问不存在的属性时候才会调用该方法,因此它不是真正的封装解决方案

5.2 __setattr__

__setattr__(self, name, value)

与__getattr__不相同,__setattr__是一种封装解决方案,允许定义分配属性值的行为,如果该属性值已经存在,则会覆盖,由此可以自定义属性值的赋值规则

def __setattr__(self, name, value):

self.name = value

# since every time an attribute is assigned, __setattr__() is called, this

# is recursion.

# so this really means self.__setattr__('name', value). Since the method

# keeps calling itself, the recursion goes on forever causing a crash

由上,代码self.name = value会调用__setattr__内置函数,所以会导致循环无限递归,正确的定义方式为

def __setattr__(self, name, value):

self.__dict__[name] = value # assigning to the dict of names in the class

# define custom behavior here

5.3 __delattr__

__delattr__(self, name)

与__setattr__相同,但是作用是删除属性而不是设置属性,为了防止无限递归,还需要采取与__setattr__相同的预防措施(在__delattr__的实现中调用del self.name将导致无限递归)

5.4 __getattribute__

__getattribute__(self, name)

不建议使用该函数,因为极少情况下可以不产生bug正确使用。

每次获取属性时候,都会调用该函数

5.5 总结

Python的魔法方法非常重要且强大,如果任意使用可能会带来破坏,因此,在已经充分了解了自定义属性访问之前,不要随意使用该魔法方法,事实上,魔法方法往往过于强大和反直觉,它存在的原因是:通过它,可以自由的做任何想做的事情,但是如果不充分熟悉,使用将会非常困难。

class AccessCounter(object):

'''A class that contains a value and implements an access counter.

The counter increments each time the value is changed.'''

def __init__(self, val):

super(AccessCounter, self).__setattr__('counter', 0)

super(AccessCounter, self).__setattr__('value', val)

def __setattr__(self, name, value):

if name == 'value':

super(AccessCounter, self).__setattr__('counter', self.counter + 1)

# Make this unconditional.

# If you want to prevent other attributes to be set, raise AttributeError(name)

super(AccessCounter, self).__setattr__(name, value)

def __delattr__(self, name):

if name == 'value':

super(AccessCounter, self).__setattr__('counter', self.counter + 1)

super(AccessCounter, self).__delattr__(name)

六、自定义序列

七、反射

八、可调用对象

在python中,函数是第一类对象,这意味着它们可以被传递给函数和方法,好像其他第一类对象一样(数值、字符串等),而这是一个非常强大的功能。

通过魔法函数__call__,可以使得实例也表现的像函数一样,以便可以调用实例,可以将实例作为参数在函数调用中传递。

8.1 __call__

__call__(self, [args...])

允许将实例进行函数调用,如下,实际作用为e(5,6)实际为调用e.__call__(5,6)

在经常需要修改实例的状态的场景下,使用__call__调用实例,修改实例状态是一种十分直观且优雅的方式

class Entity(object):

'''Class to represent an entity. Callable to update the entity's position.'''

def __init__(self, size, x, y):

self.x, self.y = x, y

self.size = size

def __call__(self, x, y):

'''Change the position of the entity.'''

self.x, self.y = x, y

if __name__ == '__main__':

e = Entity(size=10, x=1, y=2)

print(e)

e(5, 6)

print(e)

九、上下文管理

十、抽象基类

十一、描述符对象

十二、复制

十三、序列化对象

十四、结论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值