(Python Primary) - 廖雪峰Python3 - 9.面向对象高级编程

    技术2024-08-13  60

    9.面向对象高级编程

    9.1使用__slots__

    在动态语言中,实例可以轻松绑定一个属性,但直接绑定一个方法,对另一个实例是不起作用的 >>> def set_age(self, age): # 定义一个函数作为实例方法 ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s) # 给实例绑定一个方法 >>> s.set_age(25) # 调用实例方法 >>> s.age # 测试结果 25 >> s2 = Student() # 创建新的实例 >>> s2.set_age(25) # 尝试调用方法 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'set_age' 给class绑定方法 >>> def set_score(self, score): ... self.score = score ... >>> Student.set_score = set_score 使用__slot__限制实例的属性 class Student(object): __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称 >>> s = Student() # 创建新的实例 >>> s.name = 'Michael' # 绑定属性'name' >>> s.age = 25 # 绑定属性'age' >>> s.score = 99 # 绑定属性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score' 注意:对继承的子类无效,除非在子类中也定义__slots__,这样,子类实例允许定义的属性就是自身的__slots__加上父类的__slots__

    9.2使用@property

    限制属性范围的常规操作 class Student(object): def get_score(self): return self._score def set_score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value 使用@property装饰器把一个方法变成属性调用(setter/getter) class Student(object): @property def score(self): #getter return self._score @score.setter def score(self, value): #setter if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value 可定义只读属性,只定义getter class Student(object): @property def birth(self): return self._birth @birth.setter def birth(self, value): self._birth = value @property def age(self): # 只读属性 return 2015 - self._birth

    9.3多重继承

    e.g. ┌───────────────┐ │ Animal │ └───────────────┘ │ ┌────────────┴────────────┐ │ │ ▼ ▼ ┌─────────────┐ ┌─────────────┐ │ Mammal │ │ Bird │ └─────────────┘ └─────────────┘ │ │ ┌─────┴──────┐ ┌─────┴──────┐ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ MRun │ │ MFly │ │ BRun │ │ BFly │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ Dog │ │ Bat │ │ Ostrich │ │ Parrot │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ class Runnable(object): def run(self): print('Running...') class Flyable(object): def fly(self): print('Flying...') class Dog(Mammal, Runnable): # 多重继承 pass class Bat(Mammal, Flyable): # 多重继承 pass MixIn正常继承关系都是单一继承下来,但如果需要“”混入“额外功能,就需要MixIn,类似于Java中的接口 class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass

    9.4定制类

    __len__ def __len__(self): return 100 __str__ def __str__(self): return 'Student object (name: %s)' % self.name __repr__ = __str__ # __repr__是为调试服务的 __iter__,用于for...in循环,返回一个Iterator迭代对象,Python就会不断调用对象的__next__获取下一个循环值,直到出翔StopIteration错误 class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例本身就是迭代对象,故返回自己 def __next__(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration() return self.a # 返回下一个值 __getitem__,用于像list一样可以取出元素 class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a >>> f = Fib() >>> f[0] 1

    ​ 进一步匹配list的slice切片方法

    class Fib(object): def __getitem__(self, n): if isinstance(n, int): # n是索引 a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): # n是切片 start = n.start stop = n.stop if start is None: start = 0 a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L

    ​ 还有__setitem__()方法可以把对象当作list/dict来对集合赋值,__delitem__()方法,用于删除某个元素

    __getattr__在没有对应属性/方法的情况下返回 class Student(object): def __getattr__(self, attr): if attr=='score': return 99

    ​ 完全动态的__getattr__链式调用

    class Chain(object): def __init__(self, path=''): self._path = path def __getattr__(self, path): return Chain('%s/%s' % (self._path, path)) def __str__(self): return self._path __repr__ = __str__ >>> Chain().status.user.timeline.list '/status/user/timeline/list' __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() My name is Michael.

    9.5使用枚举类

    使用Enum类来定义常量(每个常量都是class的一个唯一实例) from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec')) 这样我们就获得了Month类型的枚举类,可以直接使用Month.Jan来引用一个常量,或者枚举它的所有成员: for name, member in Month.__members__.items(): print(name, '=>', member, ',', member.value) #value属性则是自动赋给成员的int常量,默认从1开始计数。 >>> Jan => Month.Jan , 1 Feb => Month.Feb , 2 Mar => Month.Mar , 3 ... Enum自定义派生类 from enum import Enum, unique @unique # @unique装饰器可以帮助我们检查保证没有重复值。 class Weekday(Enum): Sun = 0 # Sun的value被设定为0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6 >>> day1 = Weekday.Mon >>> print(day1) Weekday.Mon >>> print(Weekday.Tue) Weekday.Tue >>> print(Weekday['Tue']) Weekday.Tue >>> print(Weekday.Tue.value) 2 >>> print(Weekday(1)) Weekday.Mon

    9.6使用元类

    9.6.1type()

    type()函数可以用于动态创建类,无需class…来定义 >>> def fn(self, name='world'): # 先定义函数 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class >>> h = Hello() >>> h.hello() Hello, world. type(类名,继承的父类集合tuple,方法名称与函数绑定)

    9.6.2metaclass

    metaclass用于控制类的创建行为类→实例,metaclass→类→实例创建一个list # metaclass是类的模板,所以必须从`type`类型派生: class ListMetaclass(type): def __new__(cls, name, bases, attrs): #四个参数分别为准备创建的类对象,类名,类继承的父类集合,类方法集合 attrs['add'] = lambda self, value: self.append(value) return type.__new__(cls, name, bases, attrs) class MyList(list, metaclass=ListMetaclass): pass >>> L = MyList() >>> L.add(1) >> L [1] ORM框架应用(ORM:对象关系映射) class Field(object): def __init__(self, name, column_type): self.name = name self.column_type = column_type def __str__(self): return '<%s:%s>' % (self.__class__.__name__, self.name) class StringField(Field): def __init__(self, name): super(StringField, self).__init__(name, 'varchar(100)') class IntegerField(Field): def __init__(self, name): super(IntegerField, self).__init__(name, 'bigint') class ModelMetaclass(type): def __new__(cls, name, bases, attrs): if name=='Model': return type.__new__(cls, name, bases, attrs) print('Found model: %s' % name) mappings = dict() for k, v in attrs.items(): if isinstance(v, Field): print('Found mapping: %s ==> %s' % (k, v)) mappings[k] = v for k in mappings.keys(): attrs.pop(k) attrs['__mappings__'] = mappings # 保存属性和列的映射关系 attrs['__table__'] = name # 假设表名和类名一致 return type.__new__(cls, name, bases, attrs) class Model(dict, metaclass=ModelMetaclass): def __init__(self, **kw): super(Model, self).__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'Model' object has no attribute '%s'" % key) def __setattr__(self, key, value): self[key] = value def save(self): fields = [] params = [] args = [] for k, v in self.__mappings__.items(): fields.append(v.name) params.append('?') args.append(getattr(self, k, None)) sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params)) print('SQL: %s' % sql) print('ARGS: %s' % str(args)) class User(Model): # 定义类的属性到列的映射: id = IntegerField('id') name = StringField('username') email = StringField('email') password = StringField('password') # 创建一个实例: u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd') # 保存到数据库: u.save()

    参考教程

    廖雪峰老师的Python3教程

    (转载整理自网络,如有侵权,联系本人删除,仅供技术总结使用)

    Processed: 0.009, SQL: 9