likes
comments
collection
share

特殊方法的使用

作者站长头像
站长
· 阅读数 22

特殊方法的使用

什么是特殊方法,在Python中,如果一个方法的前后各有两个_横线,例如__len__方法,这样的方法就被称为特殊方法,有时也称为魔术方法双下划线方法

请不要将任意方法设置为特殊方法,不要乱用__*__

文章介绍

  1. 通过一个纸牌对象介绍了__len__方法和__item__方法的使用
  2. 通过一个向量对象介绍了__repr__方法和一些数值操作的特殊方法
  3. 主要说明了特殊方法的一个执行方式和特殊方法一览表。

1. 一摞Python风格的纸牌

通过纸牌类来详细说明__getitem__和__len__特殊方法的使用

import collections
# 函数工厂,实现一个Card类,里面只用rank和suit这两个属性,没有其他方法
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
    # 生成2-10加JQKA的十四章牌面
    rank = [str(n) for n in range(2, 11)] + list('JQKA')
    # 生成 '黑桃','红桃','方片','梅花' 四种花色
    # split没有参数时将以空格作为分隔符
    suits = "heart spade diamonds clubs".split()

    def __init__(self):
        # 生成不包含大小王在内的52张卡片
        self._cards = [Card(rank, suit) for rank in self.rank for suit in self.suits]

    def __len__(self):
        print("__len__方法被调用了")
        return 52  # 必须返回一个integer,整数类型

    def __getitem__(self, item):
        # print(item)
        print("__getitem__方法被调用了")
        return self._cards[item]  # 返回索引对应的内容

if __name__ == "__main__":
    deck = FrenchDeck()
    print("1.打印扑克牌数量")
    print("扑克牌数量是:",len(deck))
    print("-"*11)
    print("2.获取牌堆中的第一张扑克牌")
    print("牌堆中的第一张是:", deck[0])
    print("-"*11)
    print("3.获取牌堆中的第三到第十张牌")
    # 如果此时打印__getitem__的item参数,会发现是slice(3, 11, None)这样的,这是在序列中进行切片操作的一个类
    print("第三到第十张牌分别是:", deck[3:11])
    print("-"*11)
    print("4.遍历整个牌库")
    for card in deck:
        print(card)
    print("-"*11)
    print("5.判断某张牌是否在牌堆中")
    card = Card('7', "heart")
    # 会遍历牌堆,如果发现对应数据则终止,否则遍历完
    print(card in deck)
1.打印扑克牌数量
__len__方法被调用了
扑克牌数量是: 52
-----------
2.获取牌堆中的第一张扑克牌
__getitem__方法被调用了
牌堆中的第一张是: Card(rank='2', suit='heart')
-----------
3.获取牌堆中的第三到第十张牌
__getitem__方法被调用了
第三到第十张牌分别是: [Card(rank='2', suit='clubs'), Card(rank='3', suit='heart'), Card(rank='3', suit='spade'), Card(rank='3', suit='diamonds'), Card(rank='3', suit='clubs'), Card(rank='4', suit='heart'), Card(rank='4', suit='spade'), Card(rank='4', suit='diamonds')]
-----------
4.遍历整个牌库
__getitem__方法被调用了
Card(rank='2', suit='heart')
__getitem__方法被调用了
Card(rank='2', suit='spade')
__getitem__方法被调用了
Card(rank='2', suit='diamonds')
__getitem__方法被调用了
Card(rank='2', suit='clubs')
__getitem__方法被调用了
Card(rank='3', suit='heart')
__getitem__方法被调用了
....
-----------
5.判断某张牌是否在牌堆中
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
__getitem__方法被调用了
....
True

2. 模拟数值类型之向量类

通过向量类来介绍关于数值操作的特殊方法和打印对象的__repr__特殊方法

import math

class Vector:
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __repr__(self):
        print("__repr__方法被调用了")
        return f"Vector({self.x}, {self.y})"  # 这里的返回内容必须是字符串

    def __abs__(self):
        print("__abs__方法被调用了")
        return math.hypot(self.x, self.y)

    def __bool__(self):
        print("__bool__方法被调用了")
        return self.x != 0 or self.y != 0

    def __add__(self, other):
        print("__add__方法被调用了")
        print(f"self:{self}")
        print(f"other:{other}")
        return Vector(self.x + other.x, self.y + other.y)

    def __mul__(self, other):
        print("__mul__方法被调用了")
        print(f"self:{self}")
        print(f"other:{other}")
        return Vector(self.x * other, self.y * other)

if __name__ == "__main__":
    v1 = Vector(4, 8)
    v2 = Vector(3, 4)
    v3 = Vector(0, 0)
    print("1.打印v1对象")
    print(v1)
    print("-"*20)
    print("2.获取v2对象的绝对值")
    print(abs(v2))
    print("-"*20)
    print("3.获取v3对象的真假")
    print(bool(v3))
    print("-"*20)
    print("4.v1+v2")
    v1 += v2
    print(v1)
    print("-"*20)
    print("5.v2 * 5")
    v2 *= 5
    print(v2)
1.打印v1对象
__repr__方法被调用了
Vector(4, 8)
--------------------
2.获取v2对象的绝对值
__abs__方法被调用了
5.0
--------------------
3.获取v3对象的真假
__bool__方法被调用了
False
--------------------
4.v1+v2
__add__方法被调用了
__repr__方法被调用了
self:Vector(4, 8)
__repr__方法被调用了
other:Vector(3, 4)
__repr__方法被调用了
Vector(7, 12)
--------------------
5.v2 * 5
__mul__方法被调用了
__repr__方法被调用了
self:Vector(3, 4)
other:5
__repr__方法被调用了
Vector(15, 20)

3.关于特殊方法的一些细节

1.特殊方法是如何执行的

首先要明确一点,特殊方法是给python的解释器调用的,例如一般不会通过my_objct.__len__的方式来调用__len__方法,而是通过len(my_object)来调用的(这里的my_object是你创建的对象)。 其次特殊方法的调用是隐式的,例如,for i in my_object,其实首先找的是my_object对象中的__iter__这个特殊方法,当对象中没有这个特殊方法时,才会去找__getitem__这个特殊方法,只不过解释器将这个流程隐藏起来了,导致看起来遍历会直接调用__getitem__一样。__str____repr__也是如此。

2. 特殊方法一览表

1. 特殊方法名称(不包含运算符)
分类方法名称
字符串(字节)表示形式__repr__ __str__ __format__ __bytes__ __fspath__
转换为数值__bool__ __complex__ __int__ __float__ __hash__ __index__
模拟容器__len__ __getitem__ __setitem__ __delitem__ __contains__
迭代__iter__ __aiter__ __next__ __anext__ __reversed__
可调用对象或执行协程__call__ __await__
上下文管理__enter__ __exit__ __aexit__ __aenter__
构造和析构实例__new__ __init__ __del__
属性管理__getattr__ __getattribute__ __setattr__ __delattr__ __dir__
属性描述符__get__ __set__ __delete__ __set_name__
抽象基类__instancecheck__ __subclasscheck__
类元编程__prepare__ __init_subclass__ __class_getitem__ __mro_entries__
2.运算符的符合和背后的特殊方法
运算符分类符号特殊方法
一元数值运算符- + abs__neg__ __pos__ __abs__
比较运算符< <= == != > >=__lt__ __le__ __eq__ __ne__ __gt__ __ge__
算数运算符+ - * / // % @ divmod() round() ** pow()__add__ __sub__ __mul__ __truediv__ __floordiv__ __mod__ __matmul__ __divmod__ __round__ __pow__
反向算数运算符(交换算术运算符的操作数)__radd__ ...
增量赋值算术运算符+= -= *= /= //= %= @= **=__iadd__ __isub__ __imul__ __itruediv__ __ifloordiv__ __imod__ __imatmul__ __ipow__
按位运算符& | ^ << >> ~__and__ __or__ __xor__ __lshift__ __rshift__ __invert__
反向按位运算符(交换按位运算符的操作数)__rand__ ...
增量按位运算符&= ...__iand__ ...
转载自:https://juejin.cn/post/7393306884558831625
评论
请登录