python类和对象高阶编程知识点

下面是关于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方法时,初始化父类中的属性

子类中扩展property

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初始化方法,即不想给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类和对象的行为与高阶编程思想,希望对大家有所帮助!!!

你可能感兴趣的:(类和对象高阶方法,笔记,python)