PYTHON进阶

6.魔术方法(1)

2021-01-09  本文已影响0人  Stone_説

目录:
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
上一篇下一篇

猜你喜欢

热点阅读