python在实现类时有很多魔法,这里做个汇总。

 

__call__

在调用该方法时,无需显示地写出方法名

class Student(object):
    def __init__(self, name):
        self.name = name

    def m(self):
        print(3)

    def __call__(self):
        self.data=3
        print('My name is %s.' % self.name)


s = Student('Michael')
s.m()   # 3                     显示地写出方法名
s()     # My name is Michael.   无需显示地写出方法名

 

__dict__

含义见代码

class B(object):
    foo=1.3
print(B.__dict__)       # 类属性
# {'__dict__': <attribute '__dict__' of 'B' objects>,
# '__module__': '__main__',
# 'foo': 1.3, '__weakref__': <attribute '__weakref__' of 'B' objects>, '__doc__': None}

b=B()
print b.__dict__  # {}  # 实例属性

b.bar=13
print b.__dict__  # {'bar': 13}
print b.bar  # 13
###
# __dict__返回的是 dict类型,包括属性名和属性值
# 类的__dict__存储类的属性方法,不包括实例的
# 实例的__dict__只存储实例的属性方法,不包括类的,这点要注意。

print dir(B)
print dir(b)
###
# dir 返回的是一个list,只包括属性值
# dir 返回的是一个对象的所有属性

 

__getattr__

在访问不存在的属性和方法时,调用__getattr__方法

# 访问不存在的属性
class Student(object):

    def __init__(self):
        self.name = 'Michael'

    def __getattr__(self, attr):
        if attr=='score':
            return 99

s = Student()
print s.name        # 'Michael'
print s.score       # 99            # 不存在的属性去getattr里找
print s.t           # None          # getattr里找不到返回None,不报错


# 访问不存在的方法
class Student(object):

    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25       # 返回了函数的引用

s = Student()
print s.age()   # 25        # 方法也在getattr中找,但是找到的那个方法必须返回 函数 的引用
print s.age     # <function <lambda> at 0x026672B0>
# print s.ss()  # TypeError: 'NoneType' object is not callable

 

__getattribute__

属性拦截器

在python中,所有类都要继承于object,object有很多内建的属性和方法,我们自定义的类自然也继承了这些属性和方法,但是这些属性方法很少被用到,而且很多属性方法需要被用户重写才能使用,__getattribute__就是其中一个。

 

先上代码

class Student(object):
    country = "china"
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
        print("开始属性校验拦截功能")
        print(attr)
        return object.__getattribute__(self, attr) #返回属性值

s1 = Student("tom",19)
print(Student.country,s1.country,s1.name,s1.age) #调用属性,会调用__getattribute__方法

# 开始属性校验拦截功能
# country
# 开始属性校验拦截功能
# name
# 开始属性校验拦截功能
# age
# ('china', 'china', 'tom', 19)

1. 可以看到我们重写了__getattribute__,并返回了object的该方法

2. 类属性没有经过属性拦截器,实例属性经过属性拦截器,因为这是实例方法  (Student.country没有输出“开始属性校验...”)

3. __getattribute__可以被重写,从而实现定制的返回

 

重写属性拦截器

class Student(object):
    country = "china"  
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __getattribute__(self, attr): #注意:attr是传入的属性名,不是属性值
        print("开始属性校验拦截功能")
        print(attr)
        if attr == "name":  #注意这里引用原属性名不用self,直接引号引起来即可。
            print("现在开始调用的是name属性")
        elif attr =="age":
            print("age属性将被更改")
            self.age = 1000
            return 5555             # 如果这里没有return,会在后面那个return时返回1000
        else:
            print("现在调用的是其他属性")
        return object.__getattribute__(self, attr) #返回属性名

s2 = Student("tom",19)
print(s2.name,s2.age,s2.country)

# 开始属性校验拦截功能
# name
# 现在开始调用的是name属性
# 开始属性校验拦截功能
# age
# age属性将被更改
# 开始属性校验拦截功能
# country
# 现在调用的是其他属性
# ('tom', 1000, 'china')

可以看到age属性被更改。

 

__getitem__ and __setitem__

如果在类中定义了上述方法,那么该类的实例可以这样取值 c[key];如果类的实例执行c[key]运算,就会调用__getitem__方法。

class S(object):
    def __init__(self):
        self.s = 3

    def __getitem__(self, item):
        return self.s

    def __setitem__(self, key, value):
        setattr(self, key ,value)

s = S()
print s['a']        # 3
s['s'] = 100
print(s['s'])       # 100

注意,这里写的比较简单,在 getitem 时,不管 item 是什么,都返回 self.s ,所以 s['a'] 返回了3,重点是理解用法。 

 

__slots__ 槽

__str__ and __repr__

__str__ 用于显示类的真实内容,多用于类的 print

 

如果没有 __str__

class Student(object):
    def __init__(self, name):
        self.name = name
print Student('Michael')        # <__main__.Student object at 0x109afb190>
# 打印出一堆<__main__.Student object at 0x109afb190>,不好看

 

如果定义了 __str__

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name: %s)' % self.name
print Student('Michael')        # Student object (name: Michael)

显示的内容很直观了

 

但是我们平常不这么用啊,print class,很少这么用,那怎么办呢

s = Student('Michael')
s       # <__main__.Student object at 0x109afb310>

没有 print,内容还是不好看

 

直接显示变量调用的不是 __str__,而是 _repr___;

二者区别在于

__str__ 返回用户看到的字符串;

__repr__() 返回程序开发者看到的字符串,多用于调试

 

解决办法是再定义一个__repr__()。但是通常 __str__() 和 __repr__() 代码都是一样的,所以,有个偷懒的写法

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

 

 

 

未完待续...