1. __slots__
正常情况下当我们定义了一个class,然后创建了一个instance之后,我们可以给该实例绑定任何属性和方法:
from types import MethodType class Student: pass # 创建一个Student实例 stu1 = Student() # 给实例动态绑定一个属性 stu1.name = 'yxh' def set_age(self, age): # 定义一个函数 self.age = age # 给实例动态绑定一个方法 stu1.set_age = MethodType(set_age, stu1) print('his name: {}'.format(stu1.name)) # print(stu1.age) # AttributeError: 'Student' object has no attribute 'age' # 错误原因:在没有调用stu1.set_age之前,stu1是没有age属性的 stu1.set_age(17) print('his age: {}'.format(stu1.age))
考虑一种情况,出于某种原因,我们怎样才能限制实例的属性?比如说只允许对Student实例添加name、age属性,不允许再多余添加其他属性:在定义类时,定义一个特殊的__slots__属性:
class Student: __slots__ = ('name', 'age') stu = Student() stu.name = 'yxh' # 绑定name属性 stu.age = 17 # 绑定age属性 stu.gf = 'sdy' # 尝试绑定多余的gf属性 # AttributeError: 'Student' object has no attribute 'gf' def set_age(self, age): self.age = age stu.set_age = MethodType(set_age, stu) # AttributeError: 'Student' object has no attribute 'set_age'
使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的。
2. @property
其主要用处是更为方便的调用getter和setter,@property的使用比较简单,直接上代码:
class Student: def __init__(self): self.__age = 0 @property def get_age(self): print('get_age') return self.__age @get_age.setter def get_age(self, age): if not isinstance(age, int): raise ValueError('age must be an integer.') if age < 0 or age > 120: raise ValueError('age must between 0 - 120') print('nimeifude') self.__age = age stu = Student() stu.get_age = 20 # 其本质是stu.set_age(20) print(stu.get_age) # 其本质是print(stu.get_age())
3. 利用python的class中的一些特殊用途的函数定制类
__len__
class Student: def __init__(self, name, age): self.__name = name self.__age = age def __len__(self): return len(self.__name) stu1 = Student('yxh', 18) stu2 = Student('sundy', 17) print(len(stu1)) print(len(stu2))
__str__
class Student: def __init__(self, name, age): self.__name = name self.__age = age def __str__(self): return 'Student object (name: {})'.format(self.__name) print(Student('yxh', 18)) # Student object (name: yxh)
__iter__:如果一个类想被用于for...in循环,类似于list、tuple,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后for循环就会不断调用该迭代对象的__next__()方法,拿到循环的下一个值,直到遇到StopIteration错误时退出循环。
class Student: def __init__(self, name, score): self.__name = name self.__score = score def __iter__(self): return self def __next__(self): if len(self.__score) == 0: raise StopIteration() score = self.__score.pop() return score for score in Student('yxh', [90, 97, 93, 91, 89, 81, 87]): print(score)
__getitem__:按照下标取出某个元素;实现简单的切片功能。
class Student: def __init__(self, name, score): self.__name = name self.__score = score def __getitem__(self, item): return self.__score[item] print(Student('yxh', [90, 97, 93, 91, 89, 81, 87])[3])
class Fibonacci: def __getitem__(self, item): a, b = 1, 1 for i in range(item): a, b = b, a + b return a for i in range(17): print(Fibonacci()[i])
class Fibonacci: def __getitem__(self, item): if isinstance(item, int): a, b = 1, 1 for i in range(item): a, b = b, a + b return a if isinstance(item, slice): start = item.start stop = item.stop print(start, stop) if start is None: start = 0 if stop is None: stop = 7 a, b = 1, 1 fib_lst = [] for i in range(stop): if i >= start: fib_lst.append(a) a, b = b, a + b return fib_lst print(Fibonacci()[3:])
__getattr__:正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错。要避免这个错误,除了可以加上一个属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。
class Student(object): def __init__(self): self.name = 'Michael' def __getattr__(self, attr): if attr=='score': return 99
当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值。
>>> s = Student() >>> s.name 'Michael' >>> s.score 99
返回函数也是完全可以的:
class Student(object): def __getattr__(self, attr): if attr=='age': return lambda: 25
此时需要改变调用方式
>>> s.age() 25注意, 只有在没有找到属性的情况下,才调用__getattr__,已有的属性,不会在__getattr__中查找。
__call__
一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用。能不能直接在实例本身上调用呢?在Python中,答案是肯定的。任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用。
class Student(object): def __init__(self, name): self.name = name def __call__(self): print('My name is %s.' % self.name)
调用方式如下:
>>> s = Student('Michael') >>> s() # self参数不要传入 My name is Michael.
怎么判断一个变量是对象还是函数呢?其实,更多的时候,我们需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例。
>>> callable(Student()) True >>> callable(max) True >>> callable([1, 2, 3]) False >>> callable(None) False >>> callable('str') False
4. 枚举类
直接上代码:
>>> from enum import Enum >>> Week = Enum('Week', ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')) >>> for name, member in Week.__members__.items(): ... print(name, '->', member, ':', member.value) ... Sunday -> Week.Sunday : 1 Monday -> Week.Monday : 2 Tuesday -> Week.Tuesday : 3 Wednesday -> Week.Wednesday : 4 Thursday -> Week.Thursday : 5 Friday -> Week.Friday : 6 Saturday -> Week.Saturday : 7
如果需要更精确地控制枚举类型,可以从Enum派生出自定义类:
from enum import Enum, unique @unique class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6@unique装饰器可以帮助我们检查保证没有重复值。
访问这些枚举类型可以有若干种方法:
>>> day1 = Weekday.Mon >>> print(day1) Weekday.Mon >>> print(Weekday.Tue) Weekday.Tue >>> print(Weekday['Tue']) Weekday.Tue >>> print(Weekday.Tue.value) 2 >>> print(day1 == Weekday.Mon) True >>> print(day1 == Weekday.Tue) False >>> print(Weekday(1)) Weekday.Mon
5. 元类
Supplement Later