博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一篇掌握python魔法方法详解
阅读量:4302 次
发布时间:2019-05-27

本文共 13594 字,大约阅读时间需要 45 分钟。

本编研究下python的内置属性/魔法方法/魔法函数

这里说的内置属性,是指__xxx__的方法和属性,有的地方也称为魔法方法,叫法不一样。

本文概要

  • 1.__init____new__的顺序、使用?
  • 2.__getattribute__干嘛的?
  • 3.__call__作用,与callable内置函数有着怎样的关系呢?
  • 4.对象如何才能比较? __lt____gt__
  • 5.__getattr____setattr____delattr__何时触发?
  • 6.__add____iadd__区别,何时触发(++=)?什么时候会改变自身,什么时候会新建对象?
  • 7.__getitem____setitem____delitem__何时触发?
  • 8.迭代__iter____next__for-in循环的配合
  • 9.__mul____rmul____imul__何时触发?什么时候会改变自身,什么时候会新建对象?

如果上面几点问题都能回答上,那么可以跳过本篇文章了。本篇相关文章共三连弹。(端午节在家整理,有帮助记得点个👍,就是对我最大的肯定😘😘😘)

技巧:文章内容太多如何阅读?

  • 先看文章的目录(TOC),掌握文章整体脉络
  • 然后走马观花式,通篇阅读。如果文章有用,再细细研究;文章没用的直接忽略,找下篇。
    • 如果为了解决问题的,解决完了有时间再细读
    • 如果学习的,收藏起来,边学习,边敲代码实践 (别copy代码,自己敲)
  • 收藏完,下次用的时候,忘记如何使用了,到文章直接CTRL+F查找关键字

浩瀚的网络中,你我的相遇也是种缘分,看你天资聪慧、骨骼精奇,就送你一场大造化吧,能领悟多少就看你自己了。

1.常见魔法函数

常用专有属性 说明 触发方式
__init__ 构造初始化函数 创建实例后,赋值时使用,在__new__
__new__ 生成实例所需属性 创建实例时
__class__ 实例所在的类 实例.__class__
__str__ 实例字符串表示,可读性 print(类实例),如没实现,使用repr结果
__repr__ 实例字符串表示,准确性 类实例 回车 或者 print(repr(类实例))
__del__ 析构 del删除实例
__dict__ 实例自定义属性 vars(实例.__dict__)
__doc__ 类文档,子类不继承 help(类或实例)
__getattribute__ 属性访问拦截器 访问实例属性时
__delattr__(s,name) 删除name属性 调用时
__gt__(self,other) 判断self对象是否大于other对 调用时
__setattr__(s,name,value) 设置name属性 调用时
__gt__(self,other) 判断self对象是否大于other对象 调用时
__lt__(slef,other) 判断self对象是否小于other对象 调用时
__ge__(slef,other) 判断self对象是否大于或者等于other对象 调用时
__le__(slef,other) 判断self对象是否小于或者等于other对象 调用时
__eq__(slef,other) 判断self对象是否等于other对象 调用时
__call__(self,\*args) 把实例对象作为函数调用 调用时

下面分别以object和list的内置属性(魔法方法)来讲解下,其他的有需要的可以留言补充。

2.object

使用dir()函数来可以打印出有哪些属性,下面会挑一些常用的演示

演示

class People:    name: strclass MyTestCase(unittest.TestCase):    def test_attr(self):       print(dir(People))

如需要源码,可以查看。(关键代码都贴在文章里,不用看也行)

2-1.init

初始化时,调用__init__方法

演示

class People:    name: str = 'iworkh'    def __init__(self, name):        print('call...__init__')        self.name = nameclass MyTestCase(unittest.TestCase):    def test(self):        people = People('沐雨云楼') # 创建对象时,调用__init__        print(people.name)

2-2.new

__ new__ ()__ init__()之前被调用,用于生成实例对象.

利用这个方法和类属性的特性可以实现设计模式中的单例模式.单例模式是指创建唯一对象吗,单例模式设计的类只能实例化一个对象.

演示

class People:    name: str = 'iworkh'    def __new__(cls, *args, **kwargs):        print('call...__new__')        obj = object.__new__(cls)        return obj    def __init__(self, name):        print('call...__init__')        self.name = nameclass MyTestCase(unittest.TestCase):    def test(self):        people = People('沐雨云楼')  # 创建对象先调用__new__,初始化时调用__init__        print(people.name)

2-3.str

__ str__ ()用于表示对象代表的含义,返回一个字符串.实现了__ str__ ()方法.

  • 可以直接使用print语句输出对象,
  • 可以通过函数str()触发__ str__ ()的执行.这样就把对象和字符串关联起来,便于某些程序的实现,可以用这个字符串来表示某个类

演示

class People:    name: str = 'iworkh'    age: int = 20    def __str__(self) -> str:        print("call...__str__")        return "{
{'name': {}, 'age': {}}}".format(self.name, self.age)class MyTestCase(unittest.TestCase): def test(self): people = People() print(people) # {'name': iworkh, 'age': 20} print(str(people)) # {'name': iworkh, 'age': 20}

2-4.doc

类文档,子类不会继承

help(类或实例)会触发

演示

class People:    """    人    """    name: str = 'iworkh'    age: int = 20    def say(self):        """        say method        """        print('say')class MyTestCase(unittest.TestCase):    def test(self):        people = People()        print(People.__doc__)  # 类: 人        print(people.__doc__)  # 对象:人        print(People.say.__doc__)  # 类:say method        print(people.say.__doc__)  # 对象:say method        print(help(People.say))  # 类:say method

2-5.dict

实例自定义属性,只有_init__和通过对象.xxx赋值的属性,才会显示

vars(实例)会触发

演示

class People:    name: str = 'iworkh'    age: int = 20    admin: bool = True    def __init__(self):        self.admin = Falseclass MyTestCase(unittest.TestCase):    def test(self):        peo = People()        print(peo.__dict__)  # {'admin': False},只有init和塞值的才会显示        print(vars(peo))  # {'admin': False}        # 塞值后        peo.name = '沐雨云楼'        print(peo.__dict__)  # 对象的__dict__ , {'admin': False, 'name': '沐雨云楼'}        print(vars(peo))  # {'admin': False, 'name': '沐雨云楼'}        print(People.__dict__)  # 类的__dict__, 很多,还包括__和自己定义的

2-6.getattribute

属性访问拦截器

演示

class People:    name: str = 'iworkh'    password = 'iworkh123'    age: int = 20    def __getattribute__(self, item):        print("call .... __getattribute__")        if item == 'password':            return '保密'        else:            return object.__getattribute__(self, item)class MyTestCase(unittest.TestCase):    def test(self):        peo = People()        print(peo.name)  # iworkh        print(peo.password)  # 保密

警告

不要在__getattribute__方法中调用self.xxxx,会死循环,而应该使用object.__getattribute__(self, item)

2-7.name

__name__ 返回类名

演示

class People:    name: str = 'iworkh'class MyTestCase(unittest.TestCase):    def test_name(self):        print(People.__name__)  # 类 People

2-8.属性操作

属性操作,无非就塞值、取值、删除属性

  • __setattr__ 设置属性,设置属性值时,触发这个方法 (对象.属性, setattr)
  • __getattr__ 取属性,当属性不存在时,才会触发这个方法(对象.属性,getattr)
  • __delattr__ 删除属性,删除属性值时,触发这个方法(delattr, del)

演示

class People:    name: str = 'iworkh'    password = 'iworkh123'    def __getattr__(self, item):        print('call...__getattr__')        return 'no attribute'    def __delattr__(self, item):        print('call...__delattr__')        object.__delattr__(self, item)    def __setattr__(self, key, value):        print('call...__setattr__')        object.__setattr__(self, key, value)class MyTestCase(unittest.TestCase):    def test(self):        peo = People()        setattr(peo, 'name', '沐雨云楼')  # 塞值        # 取值        print(peo.name)  # 存在, 不会触发__getattr__        print(peo.sex)  # 不存在,调用 __getattr__        print(getattr(peo, 'name'))  # 存在, getattr方式 不会触发__getattr__        print(getattr(peo, 'sex'))  # 不存在,getattr方式 调用 __getattr__        # 删除        peo.password = "test_password"  # 塞值        del peo.password        delattr(peo, 'name')

2-9.call

定义__call__类实例对象可以像调用普通函数那样,以对象名()的形式使用。

关于call的可以阅读下这篇文章:

演示

class People:    name: str = 'iworkh'    age: int = 20    def __call__(self, **kwargs):        print('call...__call__')        self.name = kwargs['name']        self.age = kwargs['age']class MyTestCase(unittest.TestCase):    def test(self):        peo1 = People()        print(peo1.name, peo1.age, sep="......") # iworkh......20        peo1(name='沐雨云楼', age=22) # 通过对象()方式就能修改值        print(peo1.name, peo1.age, sep="......") # 沐雨云楼......22

思考:

如何取得类/对中所有属性或者方法?

  • dir:属性和方法都拿到
  • hasattr: 属性和方法都拿到
  • callable: 属性为False, 函数和方法是True

这有实现好的例子

里面的obj_dict_tool.py类的to_dic方法

还可以看下这篇文章,使用了__call__方式

2-10.repr

print的类如果没有__str__,那么就会调用__repr__

演示

class People:    name: str = 'iworkh'    age: int = 20    def __str__(self) -> str:        print("call...__str__")        return "str --> {
{'name': {}, 'age': {}}}".format(self.name, self.age) def __repr__(self): print("call...__repr__") return "repr --> {
{'name': {}, 'age': {}}}".format(self.name, self.age)class MyTestCase(unittest.TestCase): def test(self): people = People() print(people) print(str(people))

__str__代码注释后再试下

2-11.比较

对象与对象比较时,得需要比较的方法才能判断出大小,需要实现下面一些方法

  • __lt__: 小于 <
  • __gt__: 大于 >
  • __le__: 小于等于 <=
  • __ge__: 大于等于 >=
  • __eq__: 等于 =

当只定义了一个小于,没有定义大于时。在使用时也可以使用大于,会根据小于结果取反的。

演示

class People:    def __init__(self, name, age):        self.name = name        self.age = age    def __le__(self, other):        print('call ... le')        return self.age <= other.ageclass MyTestCase(unittest.TestCase):    def test(self):        people_san = People('三哥', 30)        people_three = People('张三', 30)        people_four = People('李四', 40)        people_five = People('王五', 50)        print(people_three <= people_four) # True        print(people_five >= people_three) # True        print(people_san >= people_three) # True        print(people_san <= people_three) # True        print(people_three >= people_four) # False

3.list

我们再来看下list的一些属性

属性 说明
__add__ +,返回一个新列表
__iadd__ +=,原列表上操作
__contains__ in,定义当使用成员测试运算符(in 或 not in)时的行为
__delitem__ 定义删除容器中指定元素的行为
__getitem__ 定义获取容器中指定元素的行为
__setitem__ 定义设置容器中指定元素的行为
__iter__ for定义当迭代容器中的元素的行为
__next__ for定义当迭代容器中的元素的行为
__len__ 定义当被 len() 调用时的行为
__mul__ 乘触发,返回新列表
__rmul__ 乘触发,右操作符,返回新列表
__imul__ 乘触发,改变自己
append 在列表后面追加元素
extend 在列表后面追加另一个列表的元素
insert 指定位置插入元素
copy 复制
count list.count(data) 统计列表中出现data的次数
index list.count(data) 列表中元素的索引
pop list.pop(index) 弹出指定位置的元素
remove list.remove(obj) 删除列表中指定元素
clear 清空list
reverse 列表元素倒序输出
sort list.sort(reverse=False)列表元素升序

下面只演示模仿方法

使用dir()函数来可以打印出有哪些属性,下面会挑一些常用的演示

演示

class MyTestCase(unittest.TestCase):    def test_attr(self):        print(dir([1,2,3]))

下面就来演示几个常用的魔法方法,以Alphabet类为例子,里面有个data属性为list类型,来存字母。

如需要源码,可以查看。(关键代码都贴在文章里,不用看也行)

3-1.添加

__add__ : 和+操作符对应,对象与对象相加,返回一个新列

__iadd__: 和+=操作符对应,添加一个元素,在原列表上操作

演示

class Alphabet:    data = []    def __init__(self, value) -> None:        self.data = value    def __add__(self, other):        print('call...__add__')        return Alphabet(self.data + other.data)    def __iadd__(self, other):        print("call...__iadd__")        self.data += other        return selfclass MyTestCase(unittest.TestCase):    def test(self):        list_data = ['a', 'c', 'd']        alphabet = Alphabet(list_data)        print(alphabet.data)        print("*" * 50)        # + 与 __add__        alphabet_add = Alphabet(['m', 'n'])        alphabet_sum = alphabet + alphabet_add  # 返回一个新的对象        print(alphabet.data)  # 原来的没有变 ['a', 'c', 'd']        print(alphabet_sum.data)  # ['a', 'c', 'd', 'm', 'n']        print("*" * 50)        # += 与 __iadd__        alphabet += 'x'        print(alphabet.data) # 在原对象上操作 ['a', 'c', 'd', 'x']

3-2.包含

__contains__:是否包含

演示

class Alphabet:    data = []    def __init__(self, value) -> None:        self.data = value    def __contains__(self, item):        print("call...__contains__")        return item in self.dataclass MyTestCase(unittest.TestCase):    def test(self):        list_data = ['a', 'c', 'd']        alphabet = Alphabet(list_data)        print(alphabet.data)        print('a' in alphabet) # True        print('x' in alphabet) # False

3-3.元素操作

__getitem__:取得元素

__setitem__:设置元素
__delitem__:删除元素

演示

class Alphabet:    data = []    def __init__(self, value) -> None:        self.data = value    def __getitem__(self, index):        print('call...__getitem__')        return self.data[index]    def __setitem__(self, key, value):        print('call...__setitem__')        self.data[key] = value    def __delitem__(self, key):        print("call...__delitem__")        delattr(self, key)class MyTestCase(unittest.TestCase):    def test(self):        list_data = ['a', 'c', 'd']        alphabet = Alphabet(list_data)        print(alphabet.data)        print("*" * 30) # ['a', 'c', 'd']        # 取值        print(alphabet[1])  # c 取值 会触发__getitem__        # 设置值        alphabet[1] = 'b'  # 设值 会触发__setitem__        print(alphabet.data) # ['a', 'b', 'd']        # 删除        del alphabet['data']  # 删除 等价于 delattr(alphabet, 'data')  会触发__delitem__        print(alphabet.data) # []

3-4.迭代

__iter__:迭代器,返回自己,然后for可以循环调用next方法

__next__: 每一次for循环都调用该方法(必须存在)

演示

class Alphabet:    data = []    index = -1    def __init__(self, value) -> None:        self.data = value    def __iter__(self):        print("call...__iter__")        return self    def __next__(self):        print("call...__next__")        self.index += 1        if self.index >= len(self.data):            raise StopIteration()        else:            return self.data[self.index]class MyTestCase(unittest.TestCase):    def test(self):        list_data = ['a', 'c', 'd']        alphabet = Alphabet(list_data)        print(alphabet.data)        # for循环 和 __iter__ 以及 __next__配合        for item in alphabet:            print(item)

3-5.重复

__mul__: 左操作符相乘,返回新的list,原list不变

__rmul__: 右操作符相乘,返回新的list,原list不变
__imul__: 改变原list

演示

class Alphabet:    data = []    def __init__(self, value) -> None:        self.data = value    def __mul__(self, other):        print("call...__mul__")        return Alphabet(self.data * other)    def __rmul__(self, other):        print("call...__rmul__")        return Alphabet(other * self.data)    def __imul__(self, other):        print("call...__imul__")        return self.data * otherclass MyTestCase(unittest.TestCase):    def test(self):        list_data = ['a', 'c', 'd']        alphabet = Alphabet(list_data)        print(alphabet.data)        # __mul__        alphabet_3 = alphabet * 3        print(alphabet.data)        print(alphabet_3.data)  # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd']        print("*" * 50)        # __rmul__        alphabet_r3 = 3 * alphabet        print(alphabet.data)        print(alphabet_r3.data)  # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd']        print("*" * 50)        # __imul__        alphabet *= 3  # 改变了原对象        print(alphabet)  # ['a', 'c', 'd', 'a', 'c', 'd', 'a', 'c', 'd']

3-6.len

__len__: 这个简单,但调用len(obj)会触发

演示

class Alphabet:    data = []    def __init__(self, value) -> None:        self.data = value    def __len__(self):        return len(self.data)class MyTestCase(unittest.TestCase):    def test(self):        list_data = ['a', 'c', 'd']        alphabet = Alphabet(list_data)        print(len(alphabet)) # 3

4.总结

内置属性(魔法方法)还有很多,只要记得一些常用的即可。

思考

  • 1.__init____new__的顺序、使用
  • 2.__getattribute__干嘛的?
  • 3.__call__作用,与callable内置函数有着怎样的关系呢?
  • 4.对象如何才能比较? __lt____gt__
  • 5.__getattr____setattr____delattr__何时触发?
  • 6.__add____iadd__区别,何时触发(++=)?什么时候会改变自身,什么时候会新建对象?
  • 7.__getitem____setitem____delitem__何时触发?
  • 8.迭代__iter____next__for-in循环的配合
  • 9.__mul____rmul____imul__何时触发?什么时候会改变自身,什么时候会新建对象?

如果上面几个问题能过回答上,那么恭喜您,本节内容的精髓您都掌握了。

到这内置函数和内置属性(魔法方法)都介绍完了,记得要配合 一起学习哦

5.推荐

能读到文章最后,首先得谢谢您对本文的肯定,你的肯定是对我们的最大鼓励。

你觉本文有帮助,那就点个👍

你有疑问,那就留下您的💬
怕把我弄丢了,那就把我⭐
电脑不方便看,那就把发到你📲

转载地址:http://qzhws.baihongyu.com/

你可能感兴趣的文章
RecyclerView 源码深入解析——绘制流程、缓存机制、动画等
查看>>
Android 面试题整理总结(一)Java 基础
查看>>
Android 面试题整理总结(二)Java 集合
查看>>
学习笔记_vnpy实战培训day02
查看>>
学习笔记_vnpy实战培训day03
查看>>
VNPY- VnTrader基本使用
查看>>
VNPY - CTA策略模块策略开发
查看>>
VNPY - 事件引擎
查看>>
MongoDB基本语法和操作入门
查看>>
学习笔记_vnpy实战培训day04_作业
查看>>
OCO订单(委托)
查看>>
学习笔记_vnpy实战培训day05
查看>>
学习笔记_vnpy实战培训day06
查看>>
Python super钻石继承
查看>>
回测引擎代码分析流程图
查看>>
Excel 如何制作时间轴
查看>>
股票网格交易策略
查看>>
matplotlib绘图跳过时间段的处理方案
查看>>
vnpy学习_04回测评价指标的缺陷
查看>>
ubuntu终端一次多条命令方法和区别
查看>>