Python:类的魔术方法、Hash、可视化、运算符重载、容器相关

目录

类的特殊方法

查看属性

魔术方法

 魔术方法分类

hash

bool、可视化

可视化

运算符重载

运算符重载应用场景

 容器相关


 

类的特殊方法

属性含义
__name__类、函数、方法等的名字
__module__类定义所在的模块明
__class__对象或类所属的类
__bases__类的基类元组,顺序为他们在基类列表中出现的顺序
__doc__类、函数的文档字符串,如果没有定义则为None
__mro__类的mro,class.mro()返回结果保存在__mro__中
__dict__类或实例的属性,可写字典
__subclasses__()类的子类列表,class.__subclasses__()

查看属性

方法意义
__dir__

返回类或者对象的所有成员名称列表。dir()函数就是调用__dir__()。如果提供__dir__(),则返回属性的列表,否则会尽量从__dict__属性中手机信息



 

 

  • 如果dir([obj])参数obj包含方法【__dir__()】,该方法将被调用。如果参数obj不包含【__dir__()】该方法将最大限度的收集参数信息
  • 【dir()】对于不同类型的对象具有不同的行为
    • 如果对象是模块对象,返回的列表包含模块的属性名
    • 如果对象是类型或者类对象,返回的列表包含类的属性名,及它的基类的属性名
    • 否则,返回列表包含对象的属性名,它的类的属性名和类的基类的属性名     
      #导入模块的dir
      import test1
      print(dir(test1))   #   ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
      
      #当前模块的dir
      print(dir())    #   ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'test1']
      
      #函数的dir
      def foo():
          x = 1
          pass
      
      print(dir(foo)) #   ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
      
      #类、类的继承的dir
      class A:
          a = 123
          def __init__(self,y):
              self.y = y
              pass
      
      print(sorted(dir(A)))   #   ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a']
      print(sorted(A.__dict__))   #   ['__dict__', '__doc__', '__init__', '__module__', '__weakref__', 'a']
      
      class B(A):
          pass
      
      print(sorted(dir(B)))   #   ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a']
      print(sorted(B.__dict__))   #   ['__doc__', '__module__']
      
      #类实例的对象
      
      b = B(6)
      print(sorted(dir(b)))   #   ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'a', 'y']
      print(sorted(b.__dict__))   #['y']
      
      
      #原理,在对象中收集 --》 找到类的属性收集 --》 找到父类的属性收集  --》 一直到object的类属性
      print(sorted(set(b.__dict__.keys()) | set(b.__class__.__dict__.keys() ) | set(object.__dict__)))    #   ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'y']
      
      
      #类的__dir__(self)方法,只对实例有影响
      class C(A):
          def __dir__(self):
              return ["123123"]
      
      print(sorted(dir(c)))   #   ['123123']
      print(sorted(c.__dict__))   # []

魔术方法

 魔术方法分类

  • 创建、初始化、销毁【__init__  与 __del__】
  • hash
  • bool
  • 可视化
  • 运算符重载
  • 容器和大小
  • 可调用对象
  • 上下文管理
  • 反射
  • 描述器
  • 其他杂项

hash

 

方法

意义

__hash__内建函数  hash( ) 调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash
__eq__对应 == 操作符,判断2个对象是否相等,返回bool值

看下面代码:

class A:
    def __init__(self,name,age=18):
        self.name = name

    def __hash__(self):
        return 1


print(hash(A("tom")))   #   1
print((A("tom"),A("tom")))  #   (<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>)
print([A("tom"),A("tom")])  #   [<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>]

s = {A("tom"),A("tom")}     #集合,为什么没有去重
print(s)    #   {<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>}

上面的set为什么没有被去重?

class A:
    def __init__(self,name,age=18):
        self.name = name

    def __hash__(self):
        return 1

    def __eq__(self, other):        #判断两个对象是否相等
        return self.name == other.name 

print(hash(A("tom")))   #   1
print((A("tom"),A("tom")))  #   (<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>)
print([A("tom"),A("tom")])  #   [<__main__.A object at 0x01B119D0>, <__main__.A object at 0x01B11930>]

s = {A("tom"),A("tom")}     #集合,为什么没有去重
print(s)    #   {<__main__.A object at 0x018872F0>}
print(len(s))   #   1

【__eq__】方法只是返回一个hash值作为set的key,但是去重还需要【__eq__】来判断2个对象是否相等。

hash值相等,只是hash冲突,不能说明两个对象是相等的

因此,一般来说提供【__hash__】方法是为了作为set或dict的key,所以去重要同时提供【__eq__】方法

不可hash对象【isinstance(A,collections.abc.Hashable)一定为False】,去重需要提供【__eq__】方法

print(isinstance(A,collections.abc.Hashable))   #   True

思考:list类的实例为什么不可hash?

  • 源码中有一句【__hash__ = None】,也就是如果调用【__hash__()】相当于None(),一报错。所有类都继承object,而这个类具有【__hash__()】方法的,如果一个类不能被hash,就把【__hash__】设置为None

练习:设计二维坐标类Point,使其称为2个坐标的实例是否相等?

import collections.abc

class Point:

    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __hash__(self):
        return hash((self.x,self.y))

    def __eq__(self, other):
        if self is other:
            return True
        return self.x == other.x and self.y == other.y

p1 = Point(4,5)
p2 = Point(4,5)
print(hash(p1)) #   1888254024
print(hash(p2)) #   1888254024

print(p1 is p2) #   False
print(p1 == p2) #   True

print(id(p1),id(p2))    #   8551920 14663280
print(set((p1,p2)))     #   {<__main__.Point object at 0x01297DF0>}
print(isinstance(p1,collections.abc.Hashable))  #   True

bool、可视化

方法意义
__bool__内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。没有定义【__bool__()】就找【__len__()】返回长度,非0为真。如果【__len__()】也没有定义,那么所有实例都返回真
__repr__内建函数repr()对一个对象获取字符串表达式。调用【__repr__】方法返回字符串表达,如果【__repr__】也没有定义,就直接返回object的定义就是显示内存地址信息
__str__str()函数、内建函数format(),print()函数调用,需要返回对象的字符串表达。如果没有定义,就去调用【__repr__】方法返回字符串表达,如果【__repr__】没有定义,就直接返回对象的内存地址信息
__bytes__bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象
class A :pass

print(bool(A()))    #   True
if A():
    print("Real A")

class B:
    def __bool__(self):
        return False

print(bool(B))  #   True
print(bool(B()))    #   False


class C:
    def __len__(self):
        return 0

print(bool(C()))    #   False
  • 这也是为什么空字典、空字符串、空元组、空集合、空列表等效为False的原因 

可视化

 
方法意义
__repr__内建函数repr()对一个对象获取字符串表达式。调用【__repr__】方法返回字符串表达,如果【__repr__】也没有定义,就直接返回object的定义就是显示内存地址信息
__str__str()函数、内建函数format(),print()函数调用,需要返回对象的字符串表达。如果没有定义,就去调用【__repr__】方法返回字符串表达,如果【__repr__】没有定义,就直接返回对象的内存地址信息
__bytes__bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象
class A:
    def __init__(self,name,age=18):
        self.name = name
        self.age = age

    def __repr__(self):
        return "__repr__ : {} {} ".format(self.name,self.age)

    def __str__(self):
        return " __str__ : {} {} ".format(self.name,self.age)

    def __bytes__(self):
        return " {} is {} ".format(self.name,self.age).encode()

print(A("tom")) # print函数使用   __str__ : tom 18
print([A("tom")])   #  [ ] 使用__str__,但其内部使用 [__repr__ : tom 18 ]
print(([str(A("tom"))]))    #  [ ]使用__str__,str()函数也使用__str__ [' __str__ : tom 18 ']


print("str:a")  #   str:a
s = '1'         #   1
print(s)
print(["a"],(s,))   #   ['a'] ('1',)
print({s,'a'})      #   {'1', 'a'}

print(bytes(A("tom")))  #   b' tom is 18 '

 

运算符重载

Operator模块提供以下的特殊方法,可以将类的实例使用下面的操作符来操作

运算符特殊方法含义
<,<= , == ,>,>= ,!=__lt__、__le__、__eq__、__gt__、__ge__、__ne__比较运算符
+、-、*、/、%、//、**、divmod__add__、__sub__、__mul__、__truediv__、__mod__、__floordiv__、__pow__、__divmod__算数运算符,移位,位运算也有对应的方法
+=、-=、*=、/=、%=、//=、**=__iadd__、__isub__、__imul__、__itruediv__、__imod__、__ifoordiv__、__ipow__ 
class A:
    def __init__(self,name,age = 18):
        self.name = name
        self.age = age

    def __sub__(self, other):
        return self.age - other.age

    def __isub__(self, other):
        return A(self.name,self-other)  #返回一个新的对象

tom  = A("tom")
jerry = A("jerry",16)

print(tom -jerry)   #   2
print(jerry - tom,jerry.__sub__(tom))   #   -2 -2

print(id(tom))
tom -=jerry #   24100496
print(tom.age,id(tom))  #   2 24100592

练习:完成Point类设计,实现判断点相等的方法,并完成向量的加法

  • 在直角坐标系里面,定义原点为向量的起点,两个向量和与差的坐标分别等于这两个向量相应坐标的和与差若向量的表示为(x,y)形式
class Ponit:
    def __init__(self,x,y):
        self.x = x
        self.y = y

    def __eq__(self, other):
        return self.x == other.x and other.y == other.y

    def __add__(self, other):
        return Ponit(self.x + other.x,self.y + other.y)

    def add(self,other):
        return (self.x + other.x , self.y + other.y)

    def __str__(self):
        return "Point : {} {} ".format(self.x,self.y)


p1 = Ponit(1,2)
p2 = Ponit(1,2)
points = (p1,p2)
print(points[0].add(points[1])) #   (2, 4)

#运算符重载
print(points[0] + points[1])    #   Point : 2 4

print(p1 == p2)         #   True

运算符重载应用场景

往往是用面向对象实现的类,需要做大量的运算,而运算符是这种运算在数学上最常见的表达方式。例如,上例中对+进行了运算符重载,实现了Point类的二元操作,重新定义为Point+,Point。

提供运算符重载,比直接提供加法要更加适合该领域内使用者的习惯。int类,几乎实现了所有操作符,可以作为参考

@functools.total_rodering 装饰器

  • 【__lt__】、【__le__】、【__eq__】、【__gt__】、【__ge__】、是比较大小必须实现的方法,但是全部写完太麻烦,使用@functools.total_ordering装饰器可以大大简化代码 
  • 但是要求【__eq__】必须实现,其他方法【__lt__】、【__le__】、【__gt__】、【__ge__】实现其一即可
from functools import total_ordering

@total_ordering
class Person:

    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __eq__(self, other):
        return self.age == other.age

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

tom = Person("tom",20)
jerry = Person("jerry",16)

print(tom > jerry)  #   True
print(tom < jerry)  #   False
print(tom >= jerry) #   True
print(tom <= jerry) #   False
  • 上例中大大简化了代码,但是一般来说比较实现等于或者小于方法也够了,其他可以不实现,所以这个装饰器只是看着很美好,且可能会带来性能问题,建议需要什么方法就自己创建,少用这个装饰器
    class Person:
    
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def __eq__(self, other):
            return self.age == other.age
    
        def __gt__(self, other):
            return self.age > other.age
    
        def __ge__(self, other):
            return self.age >= other.age
    
    tom = Person("tom",20)
    jerry = Person("jerry",16)
    
    print(tom > jerry)  #   True
    print(tom < jerry)  #   False
    print(tom >= jerry) #   True
    print(tom <= jerry) #   False

     

 容器相关

方法意义
__len__内建函数len(),返回对象的长度(>=0的整数),如果把对象当作容器类型看,就如同lis或者dict,bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在返回非0为真
__iter__

迭代容器时,调用,返回一个新的迭代器对象

__contains__in 成员运算符,没有实现,就调用 __iter__ 方法遍历
__getitem__实现key[key]方法,序列对象,key接受整数为索引,或者切片。对于set和dict,key为hashable,key不存在引发KeyError异常
__setitem__和__getitem__ 的访问类似,是设置值的方法
__missing__字典或其子类使用__getitem__() 调用时,key不存在执行该方法

练习:将购物车类改造成方便操作的容器类
 

class Item:
    def __init__(self,name,**kwargs):
        self.name = name
        self._spac = kwargs

    def __repr__(self):
        return "{} = {}".format(self.name,self._spac)

class Cart:

    def __init__(self):
        self.items = []

    def __len__(self):
        return len(self.items)

    def additem(self,item):
        self.items.append(item)

    def __setitem__(self, key, value):
        self.items[key] = value

    def __iter__(self):
        return iter(self.items)

    def __getitem__(self, index):
        return self.items[index]

    def __missing__(self, key):     #字典和set没有key时调用次方法
        return "Key = {}".format(key)

    def __add__(self, other):
        self.items.append(other)
        return self

    def __repr__(self):
        return str(self.items)
    def __str__(self):
        return str(self.items)
cart = Cart()
print(len(cart))    #   0
print(cart + 2 +4)  #   [2, 4]
print(len(cart))    #   2


for i in cart:
    print(i)    #   2
                #   4

print(cart[1])  #   4
cart[1] = 499

print(cart) #   [2, 499]

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值