首页 > 编程 > Python > 正文

Python高级编程-3语法最佳实践——类级

2019-11-10 19:40:47
字体:
来源:转载
供稿:网友

3.1 子类化内建函数

distinctdict.py:python中dict的子类。这个类与dict不同的是它不允许为一个key多次赋值。

class DistinctError(Exception): passclass DistinctDict(dict): def __setitem__(self, key, value): try: value_index = self.values().index(value) # keys() and values() will return # corresponding lists # as long as the dict is not changed # between the two calls # otherwise the dict type does not guaranteen the ordering. existing_key = self.keys()[value_index] if existing_key != key: raise DistinctError("This value already exists for '%s'" % str(self[existing_key])) except ValueError: pass super(DistinctDict, self).__setitem__(key, value)

3.2 访问超类中的方法

super是一个内建类型,用来访问某个对象的超类中的特性(attribute)。my_super.py

class Mama(object): def says(self): PRint 'do your homework'class Sister(Mama): def says(self): super(Sister, self).says() print 'and clean your bedroom'

Method Resolution Order (MRO): my_mro.py

L[MyClass(Base1,Base2)]=MyClass+merge(L[Base1],L[Base2],Base1,Base2)L[MyClass]是MyClass类的线性化,而merge是合并多个线性化结果的具体算法。C的线性化是C加上父类的线性化和父类列表的合并的总和。merge算法负责删除重复项并保持正确的顺序。类的mro特性(只读)用来存储线性化计算的结果class BaseBase(object): passclass Base1(BaseBase): passclass Base2(BaseBase): passclass MyClass(Base1, Base2): passdef L(klass): return [k.__name__ for k in klass.__mro__]print L(MyClass) # ['MyClass', 'Base1', 'Base2', 'BaseBase', 'object']

3.4 描述符和属性

描述符是Python中复杂特性访问的基础。它们在内部使用,以实现属性、类、静态方法和super类等。它们是定义另一个类特性可能的访问方式的类。换句话说,一个类可以委托另一个类来管理其特性。

描述符类

描述符类基于三个必须实现的特殊方法:

__set__ 在任何特性被设置的时候调用(setter)__get__ 在任何特性被读取时调用(getter)__delete__在特性上请求del时调用

这些方法将在__dict__特性之前被调用。一个使用数据描述符的实例:upper_string.py

class UpperString(object): def __init__(self): self._value = '' def __get__(self, instance, klass): return self._value def __set__(self, instance, value): self._value = value.upper()class MyClass(object): attribute = UpperString()instance_of = MyClass()instance_of.attribute = 'my value'print instance_of.attribute # MY VALUE# Now if we add a new attribute in the instance, it will be stored in its __dict__ mapping:instance_of.new_att = 1print instance_of.__dict__ # {'new_att': 1}# But if a new data descriptor is added in the class, it will take precedence over the instance __dict__:MyClass.new_att = 2print instance_of.__dict__ # {'new_att': 1}instance_of.new_att = 'other value'print instance_of.new_att # other valueprint instance_of.__dict__ # {'new_att': 'other value'}class Whatever(object): def __get__(self, instance, klass): return 'whatever'MyClass.whatever = Whatever()print instance_of.__dict__ # {'new_att': 'other value'}print instance_of.whatever # whateverinstance_of.whatever = 1print instance_of.__dict__ # {'new_att': 'other value', 'whatever': 1}

内省描述符(introspection descriptor)

这种描述符将检查宿主类签名,以计算一些信息。api.py

class API(object): def _print_values(self, obj): def _print_value(key): if key.startswith('_'): return '' value = getattr(obj, key) if not hasattr(value, 'im_func'): doc = type(value).__name__ else: if value.__doc__ is None: doc = 'no docstring' else: doc = value.__doc__ return ' %s : %s' % (key, doc) res = [_print_value(el) for el in dir(obj)] return '/n'.join([el for el in res if el != '']) def __get__(self, instance, klass): if instance is not None: return self._print_values(instance) else: return self._print_values(klass)class MyClass(object): __doc__ = API() def __init__(self): self.a = 2 def meth(self): """my method""" return 1print MyClass.__doc__ # meth : my methodinstance = MyClass()print instance.__doc__# a : int# meth : my method

元描述符(meta descriptor)

这种描述符使用类方法本身来完成值计算。chainer.py

class Chainer(object): def __init__(self, methods, callback=None): self._methods = methods self._callback = callback def __get__(self, instance, klass): if instance is None: # only for instances return self results = [] for method in self._methods: results.append(method(instance)) if self._callback is not None: if not self._callback(instance, method, results): break return resultsclass TextProcessor(object): def __init__(self, text): self.text = text def normalize(self): if isinstance(self.text, list): self.text = [t.lower() for t in self.text] else: self.text = self.text.lower() def split(self): if not isinstance(self.text, list): self.text = self.text.split() def treshold(self): if not isinstance(self.text, list): if len(self.text) < 2: self.text = '' self.text = [w for w in self.text if len(w) > 2]def logger(instance, method, results): print 'calling %s' % method.__name__ return Truedef add_sequence(name, sequence): setattr(TextProcessor, name, Chainer([getattr(TextProcessor, n) for n in sequence], logger))add_sequence('simple_clean', ('split', 'treshold'))my = TextProcessor(' My Taylor is Rich ')my.simple_clean# calling split# calling tresholdprint my.text # ['Taylor', 'Rich']# let's perform another sequenceadd_sequence('full_work', ('normalize', 'split', 'treshold'))my.full_work# calling normalize# calling split# calling tresholdprint my.text # ['taylor', 'rich']

属性(property)

知道如何将一个特性链接到一组方法上。属性采用fget参数和3个可选的参数——fset,fdel和doc。

my_property.py

class MyClass(object): def __init__(self): self._my_secret_thing = 1 def _i_get(self): return self._my_secret_thing def _i_set(self, value): self._my_secret_thing = value def _i_delete(self): print 'neh!' my_thing = property(_i_get, _i_set, _i_delete, 'the thing')instance_of = MyClass()print instance_of.my_thing # 1instance_of.my_thing = 3print instance_of.my_thing # 3del instance_of.my_thing # neh!help(instance_of)'''Help on MyClass in module __main__ object:class MyClass(__builtin__.object) | Methods defined here: | | __init__(self) | | ---------------------------------------------------------------------- | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) | | my_thing | the thing'''

overload_property.py

class FirstClass(object): def get_price(self): return '$ 500' price = property(get_price)class SecondClass(FirstClass): def get_price(self): return '$ 20'plane_ticket = SecondClass()print plane_ticket.get_price() # $ 20

3.5 槽

它允许使用__slot__特性为指定类设置一个静态特性列表,并且跳过每个类实例中__dict__列表的创建工作。它们用来为特性很少的类节省存储空间,因为将不在每个实例中创建__dict__。除此之外,它们有助于设计签名必须被冻结的类。frozen.py

class Frozen(object): __slots__ = ['ice', 'cream']print '__dict__' in dir(Frozen) # Falseprint 'ice' in dir(Frozen) # Trueglagla = Frozen()glagla.ice = 1glagla.cream = 1glagla.aha = 1 # AttributeError: 'Frozen' object has no attribute 'aha'

3.6 元编程

3.6.1 new方法

__init__在子类中不会被隐式调用。所以__new__用来确定已经在整个类层次中完成了初始化工作。my_new.py

class MyClass(object): def __new__(cls): print '__new__ called' return object.__new__(cls) # default factory def __init__(self): print '__init__ called' self.a = 1instance = MyClass()# __new__ called# __init__ calledclass MyOtherClassWithoutAConstructor(MyClass): passinstance = MyOtherClassWithoutAConstructor()# __new__ called# __init__ calledclass MyOtherClass(MyClass): def __init__(self): print 'MyOther class __init__ called' super(MyOtherClass, self).__init__() self.b = 1instance = MyOtherClass()# __new__ called# MyOther class __init__ called# __init__ called

3.6.2 metaclass方法

内建类型type是内建的基本工厂,它用来生成指定名称、基类以及包含其特性的映射的任何类。my_type.py

def my_method(self): return 1klass = type('MyClass', (object,), {'method': my_method})instance = klass()print instance.method() # 1# This is similar to an explicit definition of the class:class MyClass2(object): def my_method(self): return 1

元类:my_meta_cls.py

from ch3.api import APIdef equip(classname, base_types, dict): if '__doc__' not in dict: dict['__doc__'] = API() return type(classname, base_types, dict)class MyClass2(object): __metaclass__ = equip def alright(self): """the ok method""" return 'okay'ma = MyClass2()ma.y = 6print ma.__doc__# alright : the ok method# y : int

动态增强:enhance.py

def enhancer_1(klass): c = [l for l in klass.__name__ if l.isupper()] klass.contracted_name = ''.join(c)def enhancer_2(klass): def logger(function): def wrap(*args, **kw): print 'I log everything !' return function(*args, **kw) return wrap for el in dir(klass): if el.startswith('_'): continue value = getattr(klass, el) if not hasattr(value, 'im_func'): continue setattr(klass, el, logger(value))def enhance(klass, *enhancers): for enhancer in enhancers: enhancer(klass)class MySimpleClass(object): def ok(self): """I return ok""" return 'I lied'enhance(MySimpleClass, enhancer_1, enhancer_2)thats = MySimpleClass()print thats.ok()# I log everything !# I liedprint thats.contracted_name # MSC

元编程可以动态地在已经被实例化的类定义上创建许多不同变化,但是元类或动态增强只是一种“补丁”,它可能会很快地使精心定义的、清晰的类层次变得一团糟。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表