美文网首页PYTHON进阶
6.魔术方法(1)

6.魔术方法(1)

作者: Stone_説 | 来源:发表于2021-01-09 20:56 被阅读0次

    目录:
    1.特殊属性
    2.查看属性
    3.new,hash,bool
    4.运算符重载
    5.容器相关方法
    6.可调用对象

    1.特殊属性

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

    2.查看属性

    方法                                     意义
    __dir__                                 返回类或者对象的所有成员名称列表。dir()函数操作实例就是调用__dir__()
    locals()                                返回当前作用域中的变量字典
    globals()                               当前模块全局变量的字典
    

    dir()对于不同类型的对象obj具有不同的行为:

    1.如果对象是模块对象,返回的列表包含模块的属性名和变量名
    2.如果对象是类型或者说是类对象,返回的列表包含类的属性名,及它的祖先类的属性名
    3.如果是类的实例:
        有__dir__方法,返回可迭代对象的返回值
        没有__dir__方法,则尽可能收集实例的属性名、类的属性和祖先类的属性名
    4.如果obj不写,返回列表包含内容不同
        在模块中,返回模块的属性和变量名
        在函数中,返回本地作用域和变量名
        在方法中,返回本地作用域的变量名
    

    例1:

    #animal.py
    class Animal:
        x = 123
        def __init__(self,name):
            self._name = name
            self.__age = 10
            self.weight = 20
    
    print('animal Modules\'s names = {}'.format(dir()))
    
    # 运行结果,整个模块的属性
    animal Modules's names = [
    'Animal', 
    '__annotations__', 
    ...
    '__spec__'
    ]
    

    例2:

    import animal
    from animal import Animal
    
    class Cat(Animal):
        x = 'cat'
        y = 'abcd'
    
    class Dog(Animal):
        def __dir__(self): 
            return ['dog']  #必须返回可迭代对象
    
    tom = Cat('tom')
    dot = Dog('snoppy')
    
    # 模块名词空间内的属性
    print('Current Module\'s names = {}'.format(dir()))  
    Current Module's names = [
    'Animal', 
    ...
    'animal'
    ]
    
    # 指定模块名词空间内的属性
    print('animal Module\'s names = {}'.format(dir(animal)))  
    animal Module's names = [
    'Animal', 
    '__builtins__', 
    ...
    '__spec__'
    ]
    
    # object的字典
    print("object‘s __dict__ = {}".format(sorted(object.__dict__.keys())))
    object‘s __dict__ = [
    '__class__', 
    ...
    '__subclasshook__'
    ]
    
    # 类Animal的dir()
    print("Animal's dir() = {}".format(dir(Animal)))
    Animal's dir() = [
    '__class__', 
    ...
    '__weakref__', 
    'x'
    ]
    
    # 类Cat的dir()
    print("Cat's dir() = {}".format(dir(Cat)))
    Cat's dir() = [
    '__class__', 
    ...
    '__weakref__', 
    'x', 
    'y'
    ]
    
    # 实例tom的属性,Cat类及所有祖先类的类属性
    print(sorted(dir(tom)))  # print(sorted(tom.__dir__()))
    [
    '_Animal__age', 
    '__class__', 
    ...
    '__weakref__', 
    '_name', 
    'weight',
    'x', 
    'y'
    ]
    
    
    print(sorted(set(tom.__dict__.keys())))
    [
    '_Animal__age', 
    '_name', 
    'weight'
    ]
    
    print(set(Cat.__dict__.keys()))
    {
    'x',
    '__module__', 
    '__doc__', 
    'y'
    }
    
    print(set(object.__dict__.keys()))
    {
    '__subclasshook__', 
    ...
    '__new__', 
    '__eq__'
    }
    
    print("Dog's dir = {}".format(dir(Dog)))
    Dog's dir = [
    '__class__',
    ...
    '__weakref__', 
    'x'
    ]
    
    print(dir(Dog))
    [
    '__class__', 
    ...
    '__weakref__', 
    'x'
    ]
    
    print(dog.__dict__)
    {
    '_name': 'snoppy', 
    '_Animal__age': 10, 
    'weight': 20
    }
    

    例3:dir()的测试

    class Person:
        def show(self):
            a = 100
            t = int(a)
            print(dir())
            print(locals())
    
    def test(a=50,b=100):
        c = 150
        print(dir())
        print(locals())
    
    # Person().show()
    [
    'a', 
    'self', 
    't'
    ]
    
    {
    't': 100, 
    'a': 100, 
    'self': <__main__.Person object at 0x000002BD604AF8D0>
    }
    
    # test()
    [
    'a', 
    'b', 
    'c'
    ]
    
    {
    'c': 150, 
    'b': 100, 
    'a': 50
    }
    
    # print(dir())
    [
    'Person', 
    ...
    'test'
    ]
    
    # print(sorted(locals().keys()))
    [
    'Person', 
    ...
    'test'
    ]
    
    # print(sorted(globals().keys()))
    [
    'Person', 
    ...
    'test'
    ]
    

    3.new,hash,bool

    3.1 实例化
    方法                                                      意义
    __new__               实例化一个对象,该方法需要返回一个值,如果该值不是cls的实例,则不会调用__init__该方法永远都是静态方法__new__很少
                          使用,即使创建了该方法,也会使用return super().__new__(cls)基类object的__new__方法来创建实例并返回
    
    class A:
        def __new__(cls,*args,**kwargs):
            print(cls)
            print(args)
            print(kwargs)
            return super().__new__(cls)
    
        def __init__(self,name):
            self.name = name
    
    a = A('tom')
    print(a,a.name)
    
    # 执行结果
    <class '__main__.A'>
    ('tom',)
    {}
    <__main__.A object at 0x000001D5EB0AFF98> tom
    
    3.2 可视化
    方法                                                      意义
    __str__                            str()函数、format()函数、print()函数调用,需要返回对象的字符串表达。如果没有定义,就去调用__repr__
                                       返回字符串表达,如果__repr__没有定义,就直接返回对象的内存地址信息
    
    __repr__                           内建函数repr()对一个对象获取字符串表达。调用__repr__方法返回字符串表达,如果__repr__也没有定义,
                                       就直接返回object的定义就是显示内存地址信息
    
    __bytes__                          bytes()函数调用,返回一个对象的bytes表达,即返回bytes对象
    

    例1:

    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()
            # import json
            # return json.dumps(self.__dict__).encode()
    
    print(A('tom'))
    print('{}'.format(A('tom')))
    print([A('tom')])
    print([str(A('tom'))])
    print(bytes(A('tom')))
    
    # 运行结果
    str: tom,18
    str: tom,18
    [repr: tom,18]
    ['str: tom,18']
    b'tom is 18'
    
    3.3 去重
    方法                                                      意义
    __hash__              内建函数hash()调用的返回值,返回一个整数。如果定义这个方法该类的实例就可hash
    __eq__                对应==操作符,判断2个对象是否相等,放回bool值,定义了此方法,如果不提供__hash__方法,那么实例将不可hash
    __bool__              内建函数bool(),或者对象放在逻辑表达式的位置,调用这个函数返回布尔值。
                          没有定义__bool__(),就找__len__()返回长度,非0为真。如果__len__()也没有定义,那么所有实例都返回真
    

    hash方法只是返回一个hash值作为set的key,但是去重,还需要eq来判断2个对象是否相等。
    hash值相等,只是hash冲突,不能说明两个对象是相等的。因此,一般来说提供hash方法是为了作为set
    或者dict的key,如果去重要同时提供eq方法,不可hash对象isinstance(p1,collections.Hashable)一定为False

    例1:
    未实现__eq__,无法去重

    class A:
        def __init__(self,name,age=18):
            self.name = name
    
        def __hash__(self):
            return 1
    
        def __repr__(self):
            return self.name
    
    print({('tom',),('tom',)})
    print(hash(A('tom')))
    print({A('tom'),A('tom')})
    
    # 执行结果
    {('tom',)}
    1
    {tom, tom}
    

    例2:
    实现__eq__,可去重

    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
    
        def __repr__(self):
            return self.name
    
    print(hash(A('tom')))
    print((A('tom'),A('tom')))
    print({A('tom'),A('tom')})
    
    # 执行结果
    1
    (tom, tom)
    {tom}
    

    例3:
    bool函数实现

    class A:
        pass
    
    print(bool(A))
    print(bool(A()))
    
    if A():
        print('Real A')
    
    class B:
        def __bool__(self):
            return False
    
    print(bool(B))
    print(bool(B()))
    
    if B():
        print('Real B')
    
    class C:
        def __len__(self):
            return 0
    
    print(bool(C()))
    if C():
        print('Real C')
    
    # 运行结果
    True
    True
    Real A
    True
    False
    False
    

    4.运算符重载

    运算符                                    特殊方法                                含义
    <,<=,==,>,>=,!=            __lt__,__le__,__eq__,__gt__,__ge__,__ne__           比较运算符
    +,-,*,/,%,//,**,divmod     __add__,__sub__,__mul__,__truediv__,__mod__,        算术运算符,移位,位运算也有对应的方法
                               __floordiv__,__pow__,__divmod__        
    +=,-=,*=,/=,%=,//=,**=     __iadd__,__isub__,__imul__,__itruediv__,__imod__,__ifloordiv__,__ipow__
    

    例1:
    实例相减

    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)
    print(jerry-tom,jerry.__sub__(tom))
    print(id(tom))
    tom -= jerry
    print(tom.age,id(tom))
    
    # 运行结果
    2
    -2 -2
    1874361213056
    2 1874361213168
    
    __isub__方法定义,一般会in-place就地来修改自身,如果没有定义__isub__方法,则会调用__sub__
    注意每个方式实现时候的返回值类型
    

    5.容器相关方法

    方法                                                      意义
    __len__              内建函数len(),返回对象的长度(>=0的整数),如果把对象当做容器类型看,就如同list或者dict
                         bool()函数调用的时候,如果没有__bool__()方法,则会看__len__()方法是否存在,存在则返回非0
    __iter__             迭代容器时,调用,返回一个新的迭代器对象
    __contains__         in成员运算符,没有实现,就调用__iter__方法遍历
    __getitem__          实现self[key]访问,序列对象,key接受整数为索引,或者切片,对于set和dict,key为hashable,key不存在引发KeyError异常
    __setitem__          和__getitem__的访问类似,是设置值的方法
    __missing__          字典或其子类使用__getitem__()调用时,key不存在执行该方法
    

    例1:

    class A(dict):
        def __missing__(self, key):
            print('Missing Key : ',key)
            return 0
    
    a = A()
    print(a['k'])
    
    # 执行结果
    Missing Key :  k
    0
    

    6.可调用对象

    例1:
    Python一切皆为对象,包括函数

    def foo():
        print(foo.__module__,foo.__name__)
        
    foo() # 等价于 foo.__call__()
    
    # 执行结果
    __main__ foo
    
    # 函数即对象,对象foo加上(),就是调用此函数对象的__call__()方法
    
    方法                                  意义
    __call__              类中定义一个该方法,实例就可以像函数一样调用
    

    例2:
    调用一个实例

    class Point():
        def __init__(self,x,y):
            self.x = x
            self.y = y
    
        def __call__(self,*args,**kwargs):
            return "<Point {}:{}>".format(self.x,self.y)
    
    p = Point(4,5)
    print(p)
    print(p())
    print('***********************')
    class Adder:
        def __call__(self,*args):
            ret = 0
            for x in args:
                ret += x
            self.ret = ret
            return ret
    
    adder = Adder()
    
    print(adder(4,5,6))
    print(adder.ret)
    
    # 运行结果
    <__main__.Point object at 0x0000017A2783FFD0>
    <Point 4:5>
    ***********************
    15
    15
    

    相关文章

      网友评论

        本文标题:6.魔术方法(1)

        本文链接:https://www.haomeiwen.com/subject/fxfqaktx.html