下面是关于python类和对象的高阶编程方式与方法,说实话高阶的编程旨在大型项目中实现性能的优化,一般编程项目中使用较少
对于主要用来构成普通的数据结构时,使用__slots__属性,可极大的减少实例所占内存
class Date: __slots__ = ['year', 'month', 'day'] def __init__(self, year, month, day): self.year = year self.month = month self.day = day
- 当使用slots属性时,实例通过更加紧凑的方式来创建,实例通过一个固定大小的数组创建,slots中的属性被映射到数组的小标上
- 但是当使用slots创建时,不能再为实力添加任何属性,若要添加需要在slots中添加
- 注意不要过度使用,因为使用slots后,属性不再支持普通的类的特性
- 使用slots可以防止用户为类再增添属性,但这并不是slots设计的初衷,slots是一种内存优化机制
class A: def __init__(self): self._internal = 0 # An internal attribute self.public = 1 # A public attribute def public_method(self): ''' A public method ''' pass def _internal_method(self): pass class B: def __init__(self): self.__private = 0 #_B__private访问属性 def __private_method(self): #变为_B__private_method pass def public_method(self): pass self.__private_method() class C(B): def __init__(self): super().__init__() self.__private = 1 # Does not override B.__private # Does not override B.__private_method() def __private_method(self): pass
- 使用前置单下划线的属性均为内部实现
- 在使用双下划线时,访问属性变为其他的形式
- 使用双下划线可以在继承中发挥作用,在子类中,若有同名父类属性,但不再覆盖父类属性,而是创建子类的属性
class Person: def __init__(self, first_name): self.first_name = first_name # Getter function @property def first_name(self): return self._first_name # Setter function @first_name.setter def first_name(self, value): if not isinstance(value, str): raise TypeError('Expected a string') self._first_name = value # Deleter function (optional) @first_name.deleter def first_name(self): raise AttributeError("Can't delete attribute") #———————————————————在已有的get,set,del方法上定义property——————————————————————————— class Person: def __init__(self, first_name): self.set_first_name(first_name) # Getter function def get_first_name(self): return self._first_name # Setter function def set_first_name(self, value): if not isinstance(value, str): raise TypeError('Expected a string') self._first_name = value # Deleter function (optional) def del_first_name(self): raise AttributeError("Can't delete attribute") # Make a property from existing get/set/del methods name = property(get_first_name, set_first_name, del_first_name) #——————————————————定义动态计算属性,调用属性时,才开始计算存储—————————————————————————— import math class Circle: def __init__(self, radius): self.radius = radius @property def area(self): return math.pi * self.radius ** 2 @property def diameter(self): return self.radius ** 2 @property def perimeter(self): return 2 * math.pi * self.radius
- 使用property自定义某个属性
- 虽然在初始化时,first_name前面没有加上单下划线,但是在初始化时,调用了setter方法,进行了参数检查,所以这就已经改变了参数的赋值形式
- 定义property就是一系列相关方法的绑定操作
在子类中调用已被覆盖的父类方法,使用super()函数
class A: def __init__(self,x): self,x=x def spam(self): print('A.spam') class B(A): def __init__(self,y): super.__init__() self.y=y def spam(self): print('B.spam') super().spam() # Call parent spam()
- super函数适合在定义子类的init方法时,初始化父类中的属性
class Person(): def __init__(self, name): self.name = name # Getter function @property def name(self): return self._name # Setter function @name.setter def name(self, value): if not isinstance(value, str): raise TypeError('Expected a string') self._name = value # Deleter function @name.deleter def name(self): raise AttributeError("Can't delete attribute") class SubPerson(Person): @property def name(self): print('Getting name') #添加语句 return super().name #调用父类的getname函数 @name.setter def name(self, value): print('Setting name to', value) super(SubPerson, SubPerson).name.__set__(self, value) #调用父类的setname哈桑农户 @name.deleter def name(self): print('Deleting name') super(SubPerson, SubPerson).name.__delete__(self) #调用父类的删除机制
- 直接在子类中扩展相应的函数功能即可
- 若想创建新的实例属性,可以通过描述器
# Descriptor attribute for an integer type-checked attribute class Integer: def __init__(self, name): self.name = name def __get__(self, instance, cls): if instance is None: return self else: return instance.__dict__[self.name] def __set__(self, instance, value): if not isinstance(value, int): raise TypeError('Expected an int') instance.__dict__[self.name] = value def __delete__(self, instance): del instance.__dict__[self.name] class Point: x = Integer('x') #必须将实例对象作为另一个类的属性,不能作为实例属性,否则描述器不会起作用 y = Integer('y') def __init__(self, x, y): self.x = x self.y = y
- 描述器必须实现三个方法,get,set,del,都接受实例对象作为参数
可以在一个基类中写一个公用的__init__()函数
import math class Structure1: # Class variable that specifies expected fields _fields = [] def __init__(self, *args): if len(args) != len(self._fields): raise TypeError('Expected {} arguments'.format(len(self._fields))) # Set the arguments for name, value in zip(self._fields, args): setattr(self, name, value) # Example class definitions class Stock(Structure1): _fields = ['name', 'shares', 'price'] class Point(Structure1): _fields = ['x', 'y'] class Circle(Structure1): _fields = ['radius'] def area(self): return math.pi * self.radius ** 2 s = Stock('ACME', 50, 91.1) p = Point(2, 3) c = Circle(4.5) #————————————————————加入关键字参数—————————————————— class Structure2: _fields = [] def __init__(self, *args, **kwargs): if len(args) > len(self._fields): raise TypeError('Expected {} arguments'.format(len(self._fields))) # Set all of the positional arguments for name, value in zip(self._fields, args): setattr(self, name, value) # Set the remaining keyword arguments for name in self._fields[len(args):]: setattr(self, name, kwargs.pop(name)) #将关键字中的参数和和_fields进行绑定 # Check for any remaining unknown arguments,检查剩余元素 if kwargs: raise TypeError('Invalid argument(s): {}'.format(','.join(kwargs))) # Example use if __name__ == '__main__': class Stock(Structure2): _fields = ['name', 'shares', 'price'] s1 = Stock('ACME', 50, 91.1) s2 = Stock('ACME', 50, price=91.1) s3 = Stock('ACME', shares=50, price=91.1) # s3 = Stock('ACME', shares=50, price=91.1, aa=1) #————————————————————将不在_fields中的名称加入到属性中去————————— class Structure3: # Class variable that specifies expected fields _fields = [] def __init__(self, *args, **kwargs): if len(args) != len(self._fields): raise TypeError('Expected {} arguments'.format(len(self._fields))) # Set the arguments for name, value in zip(self._fields, args): #将fields字段与元组参数进行绑定 setattr(self, name, value) # Set the additional arguments (if any),检查额外的参数 extra_args = kwargs.keys() - self._fields for name in extra_args: setattr(self, name, kwargs.pop(name)) if kwargs: raise TypeError('Duplicate values for {}'.format(','.join(kwargs))) # Example use if __name__ == '__main__': class Stock(Structure3): _fields = ['name', 'shares', 'price'] s1 = Stock('ACME', 50, 91.1) s2 = Stock('ACME', 50, 91.1, date='8/2/2012')
- 当要定义大量简单的数据结构时不要使用__init__函数,可以考虑上述的_fields方法
假设需要验证赋值属性的类型,使用描述器来完成
# Base class. Uses a descriptor to set a value class Descriptor: def __init__(self, name=None, **opts): self.name = name for key, value in opts.items(): setattr(self, key, value) def __set__(self, instance, value): instance.__dict__[self.name] = value #下面有三个自定义类型检查数据模型,Typed,Unsigned,MaxSized # Descriptor for enforcing types class Typed(Descriptor): expected_type = type(None) def __set__(self, instance, value): if not isinstance(value, self.expected_type): raise TypeError('expected ' + str(self.expected_type)) super().__set__(instance, value) # Descriptor for enforcing values class Unsigned(Descriptor): def __set__(self, instance, value): if value < 0: raise ValueError('Expected >= 0') super().__set__(instance, value) # Descriptor for enforcing values class MaxSized(Descriptor): def __init__(self, name=None, **opts): if 'size' not in opts: raise TypeError('missing size option') super().__init__(name, **opts) def __set__(self, instance, value): if len(value) >= self.size: #不能大于规定的长度大小 raise ValueError('size must be < ' + str(self.size)) super().__set__(instance, value) #——————————————————上述为模型框架,下面给出实际数据类型———————————————————— class Integer(Typed): expected_type = int class UnsignedInteger(Integer, Unsigned): pass class Float(Typed): expected_type = float class UnsignedFloat(Float, Unsigned): pass class String(Typed): expected_type = str class SizedString(String, MaxSized): pass #———————————————————接下来用上述数据类型定义类———————————————————————— class Stock: # Specify constraints name = SizedString('name', size=8) shares = UnsignedInteger('shares') price = UnsignedFloat('price') def __init__(self, name, shares, price): self.name = name self.shares = shares self.price = price #现在如果执行某些实例属性的赋值等操作时会事先检查某些数据是否符合定义的要求
- 对于数据类型约束问题,本人了解甚少,希望感兴趣的同学可以指点迷津
实现某个实例对象的属性能够在另一个类中得以访问
下面代码实现一个简单的代理任务
class A(): def spam(self,x): pass def foo(self): pass class B(): def __init__(self): self._a=A() def spam(self,x): return self._a.spam(x) def foo(self): return self._a.foo() def bar(self): pass #——————————————————上述实现了两个方法的代理———————————————————————— #————————————若要实现多个代理,可以使用__getattr__方法—————————————————————— class B2: """ 使用 __getattr__ 的代理,代理方法比较多时候""" def __init__(self): self._a = A() def bar(self): pass # Expose all of the methods defined on class A def __getattr__(self, name): """ 这个方法在访问的 attribute 不存在的时候被调用 the __getattr__() method is actually a fallback method that only gets called when an attribute is not found""" return getattr(self._a, name) b = B2() b.bar() # Calls B2.bar() (exists on B) b.spam(42) # Calls B2.__getattr__('spam') and delegates to A.spam #————————————下面来实现代理模式—————————————————————————— # A proxy class that wraps around another object, but # exposes its public attributes class Proxy: def __init__(self, obj): self._obj = obj #此处的self_obj就是其他类的实例对象 # Delegate attribute lookup to internal obj def __getattr__(self, name): print('getattr:', name) return getattr(self._obj, name) # Delegate attribute assignment def __setattr__(self, name, value): if name.startswith('_'): super().__setattr__(name, value) else: print('setattr:', name, value) setattr(self._obj, name, value) # Delegate attribute deletion def __delattr__(self, name): if name.startswith('_'): super().__delattr__(name) else: print('delattr:', name) delattr(self._obj, name) class Spam: def __init__(self, x): self.x = x def bar(self, y): print('Spam.bar:', self.x, y) # Create an instance s = Spam(2) # Create a proxy around it p = Proxy(s) # Access the proxy print(p.x) # Outputs 2 p.bar(3) # Outputs "Spam.bar: 2 3" p.x = 37 # Changes s.x to 37
使用代理可以作为继承的替代方案
class A: def spam(self, x): print('A.spam', x) def foo(self): print('A.foo') class B: def __init__(self): self._a = A() def spam(self, x): print('B.spam', x) self._a.spam(x) def bar(self): print('B.bar') def __getattr__(self, name): return getattr(self._a, name)
注意代理的getattr方法只是被动触发,只有当所访问的方法属性不存在时,才会调用getattr方法
本质上,代理就是在一个类中,实现对另一个类的属性和方法的访问与使用,当代理中并不实现被代理对象的任何功能,只是一直调用被代理对象的属性和方法
import time class Date(): def __init__(self,year,month,day): self.year=year self.month=month self.day=day @classmethod def today(cls): t=time.localtime() return cls(t.tm_year, t.tm_mon, t.tm_mday) a = Date(2012, 12, 21) # Primary print(a) b = Date.today() # Alternate class NewDate(Date): pass c = Date.today() # Creates an instance of Date (cls=Date) d = NewDate.today() # Creates an instance of NewDate (cls=NewDate)
- 使用类方法可以实现多个构造器
创建实例对象,但想绕过init初始化方法,即不想给init参数赋值
import time class A(): def __init__(self,year,month,day): self.year=year self.month=month self.day=day a=A.__new__(A) #使用此方法,不会初始化对象的属性,即,此时调用对象属性报错 #由于a的属性不存在,所以需要手动初始化 data = {'year':2012, 'month':8, 'day':29} for key, value in data.items(): setattr(d, key, value) #使用setattr绑定实例对象属性 d.year #2012 d.month #8 #——————————————————————————绕过初始化———————————————————————— from time import localtime class Date: def __init__(self, year, month, day): self.year = year self.month = month self.day = day @classmethod def today(cls): d = cls.__new__(cls) t = localtime() d.year = t.tm_year d.month = t.tm_mon d.day = t.tm_mday return d
好啦,上面就是关于python类和对象的行为与高阶编程思想,希望对大家有所帮助!!!