python 魔法方法 赋值_python的魔法方法

python的魔法方法

python中的魔法方法是一些可以让你对类添加“魔法”的特殊方法,它们经常是两个下划线包围来命名的,比如 __init__, __call__。魔法方法可以使Python的自由度变得更高,在面向对象方面表现的更好。

python有很多魔法方法,根据其作用和特点,归纳总结为以下几类

1. 基础魔法方法

1.1 __new__

__new__ 是真正的构造函数,在实例化对象时被调用,通常你不会实现这个方法,除非你想对实例的创建进行控制,比如利用__new__ 写一个单例模式,参见文章python实现单例模式的5种方法

1.2 __init__

__init__ 被用于初始化实例对象,__new__方法创建对象后就会调用该方法对实例的属性进行初始化。很多人误以为__init__是构造函数,这是不对的,面试的时候经常拿他们两个做比较。

1.3 __del__

__del__ 是析构方法,当对象的引用计数变为0时,这个对象会被销毁掉,此时就会调用__del__方法

class T:

def __del__(self):

print("对象被销毁")

t = T()

t = 0 # t指向0, 此前创建的T的实例被销毁

print('ok')

程序输出结果

对象被销毁

ok

当变量t重新赋值为0时,此前创建的T的实例由于引用计数变为了0,对象被销毁,__del__ 被调用,而后执行print('ok')语句。

1.4 __call__

允许一个类的实例像函数一样被调用,这是一个非常重要的特性,与他相关的python概念还有callbale,参见文章python callable概念

下面的示例代码向你展示如何使用__call__

class T:

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

print("类的实例可以像函数一样被调用")

t = T()

t()

1.5 __len__

class T:

def __len__(self):

return 100

t = T()

print(len(t))

len是内置函数,len(t) 会调用实例的__len__方法

1.6 __str__

当被 str() 调用时, 调用该方法

class T:

pass

t = T()

print(str(t)) # <__main__.t object at>

如果你没有实现__str__方法,那么会调用默认的__str__,以固定格式返回对象的描述,下面是一个重载后的示例

class T:

def __str__(self):

return f"我是{self.__class__.__name__}的一个实例"

t = T()

print(str(t)) # 我是T的一个实例

1.7 __hash__

当被 hash() 调用时, 执行__hash__, 很少有机会来实现这个魔法方法。

1.8 __bool__

class T:

def __init__(self, age):

self.age = age

def __bool__(self):

return self.age > 18

print(bool(T(17)), bool(T(19))) # False True

1.9 __format__

__format__ 在格式化字符串方面是非常有用的

class Point:

def __init__(self, x, y):

self.x = x

self.y = y

def __format__(self, code):

return 'x: {x}, y: {y}'.format(x = self.x, y = self.y)

point = Point(3, 4)

print("this point is {p}".format(p=point)) # this point is x: 3, y: 4

2. 属性相关的方法

2.1 __getattr__

实例的属性存储在__dict__中,如果访问的属性不存在,就会调用__getattr__方法,如果没有实现这个方法,则会抛出AttributeError异常。

class Point:

def __init__(self, data):

self.data = data

def __getattr__(self, item):

return self.data.get(item)

point = Point({'x': 3, 'y': 4})

print(point.x) # 3

print(point.y) # 4

print(point.z) # None

point实例并不存在x,y,z这三个属性,因此都会调用__getattr__来返回数据,可以利用这个机制来返回属性

2.2 __getattribute__

访问实例的属性时,不论属性是否存在,都将会调用__getattribute__方法

class Point:

def __init__(self, x, y):

self.x = x

self.y = y

def __getattribute__(self, item):

if item == 'x':

return object.__getattribute__(self, item)

else:

raise AttributeError(f'不能访问{item}')

point = Point(3, 4)

print(point.x) # 3

print(point.y) # 异常

假设你只希望x属性可以被访问,y属性无法被访问,那么你可以通过__getattribute__来达到目的

2.3 __setattr__

对实例的属性进行赋值时,调用__setattr__方法

class Point:

def __init__(self, x, y):

self.x = x

self.y = y

def __setattr__(self, key, value):

if key == 'x':

value = abs(value)

object.__setattr__(self, key, value)

point = Point(3, 4)

point.x = -9

print(point.x) # 9

如果对x进行赋值,不论赋给它的值是多少,都要转成正整数。

2.4 __delattr__

class Point:

def __init__(self, x, y):

self.x = x

self.y = y

def __delattr__(self, item):

print(f"{item}属性被删除了")

object.__delattr__(self, item)

point = Point(3, 4)

del point.x # 删除x属性

print(point.x) # 引发异常

2.5 __dir__

当被dir函数调用时,实例的__dir__方法被执行,默认会返回实例所拥有的属性和方法,一般来说,没有必要实现这个方法

2.6 __set__, __get__ , __delete__

这三个方法放在一起讲是因为他们都涉及到描述器的概念,如果一个类定义了三者中的一个方法,那么这个类的实例就是描述器。

class Coord():

def __init__(self, name):

self.name = name

def __get__(self, instance, cls):

return instance.__dict__[self.name]

def __set__(self, instance, value):

if not isinstance(value, (int, float)):

try:

value = int(value)

except:

try:

value = float(value)

except:

raise AttributeError(f"{value}不是坐标值")

instance.__dict__[self.name] = value

class Point:

x = Coord('x')

y = Coord('y')

def __init__(self, x, y):

self.x = x

self.y = y

point = Point('3', 4)

print(point.x) # 3

point.y = 'k' # 引发异常

在这个实例中,x, y 是类Point的类属性,且他们都是描述器,描述器是一种代理机制,当对一个描述器赋值时

self.x = 3

根据描述器的协议,上面的代码等价于

Coord.__set__(x, self, 3)

这样就实现了对属性赋值的检查。

2.7 __enter__() 和__exit__

如果一个对象实现了__enter__() 和__exit__(), 那么它就是一个上下文管理器, 上下文管理在python中有着非常重要的位置,你平时使用with语句打开一个文件时,用的正是上下文管理。

通常__enter__() 会返回对象管理器本身,在进入with语句块执行代码之前,先要执行__enter__(), 从with语句块退出时执行__exit__(),做一些清理工作。

关于上下文管理器,请参考文章python with 语句

3. 比较操作符

比较操作符包括:

__lt__(self, other)定义小于号的行为:x < y

__le__(self, other)定义小于等于号的行为:x <= y

__eq__(self, other)定义等于号的行为:x == y

__ne__(self, other)定义不等号的行为:x != y

__gt__(self, other)定义大于号的行为:x > y

__ge__(self, other)定义大于等于号的行为:x >= y

下面的示例演示__lt__ 和 __eq__方法

from math import sqrt

class Point:

def __init__(self, x, y):

self.x = x

self.y = y

def __lt__(self, other):

return sqrt(self.x**2 + self.y**2) < sqrt(other.x**2 + other.y**2)

def __eq__(self, other):

return sqrt(self.x ** 2 + self.y ** 2) == sqrt(other.x ** 2 + other.y ** 2)

point1 = Point(3, 4)

point2 = Point(4, 3)

point3 = Point(3.5, 3.5)

print(point1 < point3)

print(point1 == point2)

4. 容器类型

4.1 __getitem__, __setitem__, __delitem__

__getitem__定义获取容器中指定元素的行为,相当于 self[key],__setitem__定义设置容器中指定元素的行为,相当于 self[key] = value,__delitem__)定义删除容器中指定元素的行为,相当于 del self[key]。

你应该已经注意到,这种用法和字典非常相似,实际上字典就是实现了这三个魔法方法,才提供了以[]为基础的操作。

class MyData():

def __init__(self, data={}):

self.data = data

def __getitem__(self, item):

return self.data.get(item)

def __setitem__(self, key, value):

self.data[key] = value

def __delitem__(self, key):

del self.data[key]

my_data = MyData()

my_data['name'] = '小刚'

my_data['age'] = 14

print(my_data['name']) # 小刚

print(my_data['age']) # 14

4.2 __iter__, __next__

如果一个对象实现了__iter__, 那么它就是一个可迭代对象,如果既实现__iter__ 又 实现__next__,那么它就是一个迭代器。

from collections import Iterable, Iterator

class Color(object):

def __init__(self):

self.index = -1

self.colors = ['red', 'white', 'black', 'green']

# 返回对象本身

def __iter__(self):

self.index = -1

return self

def __next__(self):

self.index += 1

if self.index >= 4:

raise StopIteration

return self.colors[self.index]

color_object = Color()

# 判断是否为可迭代对象

print(isinstance(color_object, Iterable)) # True

# 判断是否为迭代器

print(isinstance(color_object, Iterator)) # True

# 遍历输出所有颜色

for color in color_object:

print(color)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值