2021-12-02 学习笔记:Python魔术方法

Python魔术方法

2021-11-30 学习内容整理

魔术方法:

  • 以两个下划线开头、两个下划线结尾的方法,常见的如__init__
  • 魔术方法会在类或对象的某些事件发生后自动执行,如果希望在这些事件节点定制化一些特殊功能,可以对这些方法进行重写;

python中常见的魔术方法:

  • 构造与初始化
  • 类的表示
  • 访问控制
  • 比较操作
  • 容器类操作
  • 可调用对象
  • 序列化

构造与初始化

__init__方法可以定义一个对象的初始化操作;实际在实例化一个类的时候,还有一个__new__的方法会被调用;在对象生命周期的另一端,还有一个__del__方法;

__new__

__new__(cls,[...])

  • 是在一个对象实例化的时候所调用的第一个方法,它才是真正意义上的构造方法;
  • 第一个参数是类对象,其他参数会直接传递给__init__方法;
  • __new__方法还决定了是否使用该__init__方法,因为__new__可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例;
  • 如果__new__方法没有返回实例对象,则__init__方法不会被调用;
class Person(object):
    def __new__(cls, *args, **kwargs):
        # 返回对象 其他参数会传给__init__方法
        return object.__new__(cls)
    def __init__(self, name, age):
        self.name = name
        self.age = age

p = Person("A",16)

使用场景:

  • 需要继承内置类时,比如int str tuple,只能通过__new__来初始化数据;
class float_g(float):
    def __new__(cls, kg):
        return float.__new__(cls, kg*1000)

a = float_g(50) # 50kg 转 g,由于继承了float,所以可以直接运算

__init__

__init__构造器,当一个实例被创建的时候调用的初始化方法;

__del__()

__del__()析构器,当一个实例被销毁时自动调用的方法;

class Washer:
    def __del__(self):
        print('deleted')

类的表示

  • __str__() / __repr__()
  • __bool__()

__str__() / __repr__()

这两个方法都是用来描述类或对象信息的,如果直接打印一个对象,正常输出的是对象的地址;重新定义类中的这两个方法,打印出来的结果就是方法的返回信息;

class Washer:
    def __repr__(self):
        return "repr"

    def __str__(self):
        return "str"

区别在于:

  • __repr__的目标是准确性,其结果是让解释器用的;
  • __str__的目标是可读性,是对类或对象状态的说明,是给人看的,print会优先使用这个方法的返回值;

__bool__()

当调用bool(obj)时,会调用__bool__()方法,返回True或False;

class Person(object):
    def __init__(self, uid):
        self.uid = uid
    def __bool__(self):
        return self.uid > 10

bool(Person(4)) # False

访问控制

  • __setattr__:定义一个属性被设置时的行为;
  • __getattr__:定义当用户试图获取一个不存在的属性时的行为;
  • __delattr__:删除某个属性是调用;
  • __getattribute__:访问任意属性或方法时调用;
class Person(object):

    def __setattr__(self, key, value):
        """属性赋值"""
        if key not in ('name', 'age'):
            return
        if key == 'age' and value < 0:
            raise ValueError()
        super(Person, self).__setattr__(key, value)

    def __getattr__(self, key):
        """访问某个不存在的属性"""
        return 'unknown'

    def __delattr__(self, key):
        """删除某个属性"""
        if key == 'name':
            raise AttributeError()
        super().__delattr__(key)

    def __getattribute__(self, key):
        """所有属性/方法调用都经过这里"""
        print('...')
        if key == 'money':
            return 100
        elif key == 'hello':
            return self.say
        return super().__getattribute__(key)

p1 = Person()
p1.name = '张三'  # 调用__setattr__
p1.age = 20  # 调用__setattr__
print(p1.name, p1.age)  # 张三 20

setattr(p1, 'name', '李四')	# 调用__setattr__
setattr(p1, 'age', 30)  # 调用__setattr__
print(p1.name, p1.age)  # 李四 30

print(p1.sex)  # 调用__getattr__
print(p1.hello)
# 上面只要是访问属性的地方,都会调用__getattribute__方法

...
...
张三 20
...
...
李四 30
...
unknown
...
...
unknown

比较操作

  • __eq__()
  • __ne__()
  • __lt__()
  • __gt__()

__eq__()

__eq__()方法,用于判断两个对象是否相等;

class Person(object):
    def __init__(self, uid):
        self.uid = uid
    def __eq__(self, other):
        return self.uid == other.uid

p1 = Person(1)
p2 = Person(2)
p3 = Person(1)

p1 == p2 # False
p1 == p3 # True

__ne__()

判断两个对象是否不相等,用法和__eq__()一致;

def __ne__(self, other):
    return self.uid != other.uid

__lt__() & __gt__()

__lt__() 小于;__gt__() 大于;

class Person(object):
    def __init__(self, uid):
        self.uid = uid
    
    def __lt__(self, other):
        return self.uid < other.uid

    def __gt__(self, other):
        return self.uid > other.uid

容器类操作

常用的容器类型:字典 元组 列表 字符串,它们都可迭代(实现了容器协议)

  • __setitem__(self, key, value)
    • 设置容器元素,相当于self[key] = value
  • __getitem__(self, key)
    • 获取容器元素,相当于self[key]
  • __delitem__(self, key)
    • 删除容器元素,相当于del self[key]
  • __len__(self)
    • len()调用时,返回容器元素的个数;
  • __iter__(self)
    • 迭代容器中元素的行为;
  • __contains__(self, item)
    • 定义使用成员测试运算符innot in时的行为;
  • __reversed__(self)
    • reversed()调用时的行为;
class MyList(object):
    def __init__(self, values=None):
        self.values = values or []
        self._index = 0
    def __setitem__(self, key, value):
        self.values[key] = value
    def __getitem__(self, key):
        return self.values[key]
    def __delitem__(self, key):
        del self.values[key]
    def __len__(self):
        return len(self.values)
    def __iter__(self):
        return self
    def __next__(self):
        # __iter__返回self时,必须实现此方法 定义迭代的具体细节
        if self._index >= len(self.values):
            raise StopIteration()
        value = self.values[self._index]
        self._index += 1
        return value
    def __contains__(self, item):
        return item in self.values
    def __reversed__(self):
        return MyList(list(reversed(self.values)))

__iter__方法的返回值有两种:

  • 返回iter(obj):代表使用obj对象的迭代协议,一般是obj是内置的容器对象;
  • 返回self,代表迭代的逻辑由本类实现,此时需要重写next方法,实现自定义的迭代逻辑;

可调用对象

Python中有一个特殊的方法可以让类的实例的行为表现的像函数一样,即__call__(self,[args...])

class Circle(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __call__(self, x, y):
        self.x = x
        self.y = y

a = Circle(10,20)
a.x,a.y # 10 20

a(100,200)
a.x,a.y # 100 200

通过__call__方法,python中的实例,也可以被调用;

这个方法通常会用在装饰器、元类编程场景;

序列化

Python中的序列化模块是pickle,当使用这个模块序列化一个实例对象时,可以通过__getstate__()__setstate__()方法实现自己的逻辑;

import pickle

class Person(object):
    def __init__(self, name, age, birthday):
        self.name = name
        self.age = age
        self.birthday = birthday

    def __getstate__(self):
        # 执行 pick.dumps时 忽略age属性
        return {
            'name': self.name,
            'birthday':self.birthday
        }       
    def __setstate__(self, state):
        # 执行 pick.loads时 忽略age属性
        self.name = state['name'] 
        self.birthday = state['birthday']

person = Person('aa', 20 , (2017,2,23))

p_person = pickle.dumps(person)

n_person = pickle.loads(p_person)


使用pickle序列化对象时,要仔细些,避免一些可能的坑

你可能感兴趣的:(Python,python,开发语言,后端)