""" --*第三章 语法最佳实践--类级别以上*-- """ '''3.1 子类化内置类型''' # object内置类型是所有内置类型的共同祖先 # 也是所有没有显式指定父类的用户自定义类的共同祖先 # 当需要实现与某个内置类型具有相似行为的类时,最好的方法是将这个内置类型子类化 # 定义一个类 是dict类型的子类 新类大部分行为与普通的dict相同 # 但不允许多个键对应相同的值 若重复会引起ValueError的子类错误 class DistinctError(ValueError): """若添加重复值,引发这个错误""" class distinctdict(dict): """不接收重复值的字典""" def __setitem__(self, key, value): if value in self.values(): if ( (key in self and self[key] != value) or key not in self ): raise DistinctError( """this value already exist for different key""" ) super().__setitem__(key, value) my = distinctdict() my['key'] = 'value' print(my) # {'key': 'value'} # my['other_key'] = 'value' # print(my) # 报错 # in __setitem__ # """this value already exist for different key""" # __main__.DistinctError: this value already exist for different key # 许多类都是对内置类型的部分实现 它们作为子类速度更快 # list类型用来管理序列,一个类需要在内部处理序列,可以对list进行子类化 class Folder(list): def __init__(self, name): self.name = name def dir(self, nesting=0): offset = " " * nesting print('%s%s/' % (offset, self.name)) for element in self: if hasattr(element, 'dir'): element.dir(nesting + 1) else: print('%s %s' % (offset, element)) tree = Folder('project') tree.append('README.md') print(tree.dir()) # project/ # README.md src = Folder('src') src.append('script.py') tree.append(src) print(tree.dir()) # project/ # README.md # src/ # script.py '''3.2 访问超类中的方法''' # super是一个内置类 可用于访问属于某个对象的超类的属性 class Mama: # 旧的写法 def says(self): print('do your homework') class Sister(Mama): def says(self): Mama.says(self) # 调用超类的says()方法,并将self作为参数传入 print('and clean your bedroom') print(Sister().says()) # 输出 # do your homework # and clean your bedroom # super()用法 class Sister(Mama): def says(self): super().says() print('and clean your room') print(Sister().says()) # 输出同上 # super第二个参数可选,若只提供第一个参数, # 那么super返回的是一个未绑定的类型 class Pizza: def __init__(self, toppings): self.toppings = toppings def __repr__(self): return "Pizza with " + " and ".join(self.toppings) @classmethod def recommend(cls): """推荐任意馅料(toppings)的某种披萨。""" return cls(['spam', 'ham', 'eggs']) class VikingPizza(Pizza): @classmethod def recommend(cls): """推荐与super相同的内容,但多加了午餐肉(spam)。""" recommended = super(VikingPizza).recommend() recommended.toppings += ['spam'] * 5 return recommended # 使用super易犯的错误 # 1.混用super与显示类调用 class A: def __init__(self): print("A", end=" ") super().__init__() class B: def __init__(self): print("B", end=" ") super().__init__() class C(A, B): def __init__(self): print('C', end=" ") A.__init__(self) B.__init__(self) print(C()) # 输出 # C A B B <__main__.C object at 0x0000000002769438> # C类使用__init__方法调用他的基类,使得B类被调用两次 # C的实例调用了A.__init__(self),使得super(A,self).__init()调用了B.__init__()方法 # 2.不同种类的参数 # 使用super的另一个问题是初始化过程中参数传递 # 子类要与父类的签名相匹配 '''3.3 高级属性访问模式''' # 1.描述符:允许你自定义在引用一个对象的属性时应该完成的事情 # 它是一个类,定义了另一个类的属性访问方式。一个类可以将属性委托给另一个类 # 描述符类基于3个特殊方法,这三个方法组成了描述符协议 # __set__:在设置属性时将调用这一方法,setter # __get__:在读取属性时将调用这一方法,getter # __delete__:对属性调用del时将调用这一方法 # 实现__get__()和__set__()的描述符被称为数据描述符 # 实现__get__()称为非数据描述符 # 属性查找时,这个协议的实际上由对象的特殊方法__getattribute__()调用。 # 每次通过点号或者getattr函数调用来执行这样的查找时,都会隐式的调用__getattribute__() # 按下列顺序查找该属性 # [1]验证该属性是否为实例的类对象的描述符 # [2]如果不是,就查看该属性是否能在实例对象的__dict__中找到 # [3]最后,查看该属性是否为实例的类对象的非数据描述符 # 数据描述符优于__dict__查找,而__dict__查找优先于非数据描述符 class RevealAccess(object): """一个数据描述符,正常设定值并返回值,同时打印出记录访问信息 """ def __init__(self, initval=None, name='var'): self.val = initval self.name = name def __get__(self, obj, objtype): print('Retrieving', self.name) return self.val def __set__(self, obj, val): print('Updating', self.name) self.val = val class MyClass(object): x = RevealAccess(10, 'var "x"') y = 5 m = MyClass() print(m.x) # 输出 Retrieving var "x" 10 m.x = 20 # 输出 Updating var "x" # 若一个类的某个属性有数据描述符,那么每次查找这个属性时 # 都会调用描述符的__get__()方法并返回他的值 # 每次对这个属性赋值时都会调用__set__() # 函数对象是非数据描述符 # 如果没有__dict__优先于非数据描述符,将不可能在运行时在已经构建好的实例上动态覆写特定的方法 # 现实例子-延迟求值属性 # 将类属性的初始化延迟到被实例访问时。 # 如果这些属性的初始化依赖全局应用上下文的话,这一点可能有用 # 另一使用场景是初始化代价很大,但在导入类的时候不知道是否会用到这个属性 class IninOnAccess: def __init__(self, klass, *args, **kwargs): self.klass = klass self.args = args self.kwargs = kwargs self._initialized = None def __get__(self, instance, owner): if self._initialized is None: print('initialized!') self._initialized = self.klass(*self.args, **self.kwargs) else: print('cached') return self._initialized class MyClass: lazily_initialized = IninOnAccess(list, 'argument') m= MyClass() print(m.lazily_initialized) # initialized! # ['a', 'r', 'g', 'u', 'm', 'e', 'n', 't'] print(m.lazily_initialized) # cached # ['a', 'r', 'g', 'u', 'm', 'e', 'n', 't'] # 2.property:提供一个内置的描述符类型,将一个属性链接到一组方法上(把方法变成属性) # 接受4个可选参数:fget、fset、fdel、doc。 # 最后一个参数可以用来定义一个链接到属性的docstring,就像一个方法一样。 # eg:控制方法两种,一种是直接访问保存两个顶点的属性,另一种是利用width和height class Rectangle: def __init__(self, x1, y1, x2, y2): self.x1, self.y1 = x1, y1 self.x2, self.y2 = x2, y2 def _width_get(self): return self.x2 - self.x1 def _width_set(self, value): self.x2 = self.x1 + value def _height_get(self): return self.y2 - self.y1 def _height_set(self, value): self.y2 = self.y1 + value width = property( _width_get, _width_set, doc="rectangle width measured from left" ) height = property( _height_get, _height_set, doc="rectangle height measured from top" ) def __repr__(self): return "{}({}, {}, {}, {})".format( self.__class__.__name__, self.x1, self.y1, self.x2, self.y2 ) rectangle = Rectangle(10, 10, 25, 34) print(rectangle.width, rectangle.height) print(rectangle) # 在使用继承过程中必须要注意,创建的属性是利用当前类的方法实时创建 # 不会使用派生类中覆写的方法 # 例如: class MetricRectangle(Rectangle): def _width_get(self): return "{} meters".format(self.x2 - self.x1) print(Rectangle(0, 0, 100, 100).width) # 输出 # 100 # 解决这一问题,要在派生类中覆写整个property class MetricRectangle(Rectangle): def _width_get(self): return "{} meters".format(self.x2 - self.x1) width = property(_width_get, Rectangle.width.fset) print(MetricRectangle(0, 0, 100, 100).width) # 输出 # 100 meters # 创建property最佳语法是使用property作为装饰器,减少类内部方法签名的数量 # 并提高代码的可读性和可维护性 class Rectangle: def __init__(self, x1, y1, x2, y2): self.x1, self.y1 = x1, y1 self.x2, self.y2 = x2, y2 @property def width(self): """rectangle height measured from left""" return self.x2 - self.x1 @width.setter def width(self, value): self.x2 = self.x1 + value @property def height(self): """rectangle height measured from top""" return self.y2 - self.y1 @height.setter def height(self, value): self.y2 = self.y1 + value print(Rectangle(0, 0, 100, 100).width) # 输出 100
python高级编程 python 笔记3
最新推荐文章于 2024-06-17 17:35:06 发布