【python学习笔记】面向对象之进阶部分2

描述符

描述符的本质就是一个类,在这个类中,至少实现了 __get__(),__set__(),__delete__()中一个,这个也被称为描述符协议
① __get__():调用一个属性时,触发
② __set__():为一个属性赋值时,触发
③ __delete__():采用del删除属性时触发

#结构
class Foo: #Foo类,它实现了三种方法,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass
    def __set__(self, instance, value):
        pass
    def __delete__(self, instance):
        pass

描述符的作用:用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

#############################################################
class Foo:
	def __get__(self,instance,owner):
		print('get')
	def __set__(self,instance,value):
		print('set')
	def __delete__(self,instance):
		print('delete')

f1 = Foo()
f1.name = 'laowang'
f1.name
del f1.name
#这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法
##############################################################
#描述符Boy
class Boy:
	def __get__(self,instance,owner):
		print('Boy get')
	def __set__(self,instance,value):
		print('Boy set')
	def __delete__(self,instance):
		print('Boy delete')
#描述符Girl
class Girl:
	def __get__(self,instance,owner):
		print('Girl get')
	def __set__(self,instance,value):
		print('Girl set')
	def __delete__(self,instance):
		print('Girl delete')
#定义People类
class People:
	gen1 = Boy() #gen1属性被Boy类代理
	gen2 = Girl() #gen2属性被Girl类代理
	def __init__(self,gen1,gen2):
		self.gen1 = gen1
		self.gen2 = gen2
#实例化
p1 = People('laowang',18) #赋值触发Boy和Girl的set方法

#描述符Boy的使用
p1.gen1 #Boy get
p1.gen1 = 'laozhao' #Boy set
del p1.gen1 #Boy delete

#描述符Girl的使用
p1.gen2 #Girl get
p1.gen2 = 'laoli' #Girl set
del p1.gen2 #Girl delete
#################################################################
#########练习
#简单演示
class Foo: 
	def __get__(self,instance,owner):
		print('触发get',instance,owner)
	def __set__(self,instance,value):
		print('触发set',instance,value)
	def __delete__(self,instance):
		print('触发delete',instance)

class Bar:
	x = Foo()
	def __init__(self,n):
		self.x = n

b1 =Bar(10) #触发set <__main__.Bar object at 0x00000212AB3E77B8> 10
print(b1.__dict__) #{}

print(Bar.__dict__) #'x': <__main__.Foo object at 0x0000028BD3E57320>

b1.x #触发get <__main__.Bar object at 0x0000023B653E4400> <class '__main__.Bar'>
b1.x = 1 #触发set <__main__.Bar object at 0x0000023B653E4400> 1
del b1.x #触发delete <__main__.Bar object at 0x0000023B653E4400>
#
class Foo: 
	def __get__(self,instance,owner):
		print('触发get')
	def __set__(self,instance,value):
		print('触发set',instance,value)
		instance.__dict__['x'] = value #增加属性字典赋值
	def __delete__(self,instance):
		print('触发delete')

class Bar:
	x = Foo()
	def __init__(self,n):
		self.x = n #b1.x = 10 x被代理,因此执行代理描述符内部对应的set方法

b1 = Bar(10) #触发set <__main__.Bar object at 0x000001F0D35D4400> 10
print(b1.__dict__) #{'x': 10}
b1.x = 1111 #触发set <__main__.Bar object at 0x0000023BE8184438> 1111
print(b1.__dict__) #{'x': 1111}

b1.y = 222222 #没有被代理的属性,所以没有执行描述符内的set方法
print(b1.__dict__) #{'x': 111111, 'y': 222222} -->>没有被代理的属性

描述符的分类:一般分为两种:数据描述符和非数据描述符

#1.数据描述符:至少实现了__get__()和__set__()方法
class Foo:
	def __get__(self,instance,owner):
		print('get')
	def __set__(self,instance,value):
		print('set')

#2.非数据描述符:没有实现__set__()
class Foo:
	def __get__(self,instance,owner):
		print('get')

描述符分优先级:下面由高到低
① 类属性
② 数据描述符
③ 实例属性
④ 非数据描述符
⑤ 找不到的属性则触发__getattr__方法

#######################类属性 数据描述符 实例属性
class Foo: 
	def __get__(self,instance,owner):
		print('触发get')
	def __set__(self,instance,value):
		print('触发set',instance,value)
		instance.__dict__['x'] = value #增加属性字典赋值
	def __delete__(self,instance):
		print('触发delete')

class Bar:
	x = Foo()
	def __init__(self,x):
		self.x = x

#类属型 > 数据描述符
Bar.x #类属性优先级高于数据描述符,但是找不到就去找描述符伪装的类属性找,所以触发了get
Bar.x = 1 #类属性优先级高,所以先给x属性赋值,不会触发set
print(Bar.__dict__) #'x': 1
print(Bar.x) #因为类属性字典中存在x,所以优先类属型,->#1

#数据描述符 > 实例属性
b1 = Bar(10) #触发set <__main__.Bar object at 0x000001B073B272E8> 10
b1.x #触发get
b1.x = 1 #触发set <__main__.Bar object at 0x0000025665DF4358> 1
del b1.x #触发delete

#######################实例属性 非数据描述符 找不到的属性
class Foo: 
	def __get__(self,instance,owner):
		print('触发get')
	#def __delete__(self,instance):
		#print('触发delete')

class Bar:
	x = Foo()

#实例属性 > 非数据描述符
b1 = Bar()
b1.x = 1 #实例属性优先级高,执行赋值
print(b1.__dict__) #{'x': 1}


class Foo: 
	def __get__(self,instance,owner):
		print('触发get')
	#def __delete__(self,instance):
		#print('触发delete')

class Bar:
	x = Foo()
	def __getattr__(self,item):
		print('-->>')
		
#执行getattr
b1 = Bar()
b1.x = 1
b1.y #属性找不到,执行getattr
print(b1.__dict__) #{'x': 1}

描述符的使用
python对参数的赋值没有类型限制,可以通过描述符机制来实现类型限制功能

#参数的赋值无类型限制
def test(x):
	print('-->>',x)

test('laowang') #-->> laowang
test(1111) #-->> 1111

#描述符必须定义成数据描述符,非数据描述符的优先级低于实例属性
class Typed:
	def __get__(self,instance,owner):
		print('get方法')
		print('instance参数【%s】' % instance)
		print('owner参数【%s】' % owner)
	def __set__(self,instance,value):
		print('set方法')
		print('instance参数【%s】' % instance)
		print('value参数【%s】' % value)
	def __delete__(self,instance):
		print('delete方法')
		print('instance参数【%s】' % instance)

class People:
	name  = Typed()
	def __init__(self,name,age,salary):
		self.name = name
		self.age = age
		self.salary = salary
		
p1 = People('laowang',18,1000) #set方法 #instance参数【<__main__.People object at 0x000001ABD8D94390>】#value参数【laowang】
p1.name #get方法 #instance参数【<__main__.People object at 0x000002721B4C4390>】 #owner参数【<class '__main__.People'>】
p1.name = 'laowang' #set方法 #instance参数【<__main__.People object at 0x000001B9245E4400>】 #value参数【laowang】
print(p1.__dict__) #{'age': 18, 'salary': 1000}:没有name原因在于name属性被描述符给代理了

#基本实现类型限制功能
class Typed:
	def __init__(self,key):
		self.key = key
	def __get__(self,instance,owner):
		print('get方法')
		print('instance参数【%s】' % instance)
		print('owner参数【%s】' % owner)
		return instance.__dict__[self.key]

	def __set__(self,instance,value):
		print('set方法')
		print('instance参数【%s】' % instance)
		print('value参数【%s】' % value)
		#instance.__dict__['name'] = value
		if not isinstance(value,str): ###########增加类型限制功能-->>字符串类型限制
			#print('你传入的类型不是字符串')
			#return
			raise TypeError('你传入的类型不是-->>字符串') #######报错
		instance.__dict__[self.key] = value

	def __delete__(self,instance):
		print('delete方法')
		print('instance参数【%s】' % instance)
		instance.__dict__.pop(self.key)

class People:
	name  = Typed('name') #t1.__set__() self.__set__()
	#age = Typed('age')
	def __init__(self,name,age,salary):
		self.name = name
		self.age = age
		self.salary = salary

#描述符内只有print的时
#p1 = People('laowang',18,1000) #set方法 #instance参数【<__main__.People object at 0x000001ABD8D94390>】#value参数【laowang】
#print(p1.name) #get方法 #instance参数 #owner参数 #None
#p1.name = 'laowang' #set方法 #instance参数 #value参数【laowang】
#print(p1.name) #get方法 #instance参数 #owner参数 #None
#print(p1.__dict__) #{'age': 18, 'salary': 1000}

#描述符增加取值 赋值 删除值时
p1 = People('laowang',18,1000)
print(p1.__dict__) #{'name': 'laowang', 'age': 18, 'salary': 1000}
#判断类型,213不是字符串类型
p1 = People(213,18,1000) #你传入的类型不是字符串
print(p1.__dict__) #{'age': 18, 'salary': 1000}

print(p1.__dict__) #{'name': 'laowang', 'age': 18, 'salary': 1000}
print(p1.name) #laowang

print(p1.__dict__) #{'name': 'laowang', 'age': 18, 'salary': 1000}
p1.name = 'laozhao'
print(p1.__dict__) #{'name': 'laozhao', 'age': 18, 'salary': 1000}

print(p1.__dict__) #{'name': 'laozhao', 'age': 18, 'salary': 1000}
del p1.name
print(p1.__dict__) #{'age': 18, 'salary': 1000}

#增加多种自定义类型限制
class Typed:
	def __init__(self,key,expected_type):
		self.key = key
		self.expected_type = expected_type

	def __get__(self,instance,owner):
		print('get方法')
		print('instance参数【%s】' % instance)
		print('owner参数【%s】' % owner)
		return instance.__dict__[self.key]

	def __set__(self,instance,value):
		print('set方法')
		print('instance参数【%s】' % instance)
		print('value参数【%s】' % value)
		#instance.__dict__['name'] = value
		if not isinstance(value,self.expected_type): ###########增加类型限制功能-->>自定义类型限制
			raise TypeError('你传入的类型不是-->>%s' % self.expected_type) #######报错
		instance.__dict__[self.key] = value

	def __delete__(self,instance):
		print('delete方法')
		print('instance参数【%s】' % instance)
		instance.__dict__.pop(self.key)

class People:
	name  = Typed('name',str) #t1.__set__() self.__set__()
	age = Typed('age',int)
	def __init__(self,name,age,salary):
		self.name = name
		self.age = age
		self.salary = salary

p1 = People('laowang',18,1000)
print(p1.__dict__) #{'name': 'laowang', 'age': 18, 'salary': 1000}
#判断类型,213不是字符串类型
p1 = People(213,18,1000) #你传入的类型不是字符串
print(p1.__dict__) #{'age': 18, 'salary': 1000}

p1 = People('laozhao','25') #报错,因为age传入的类型不是int

类的装饰器

##函数 装饰器 (无参数)
def deco(func):
	print('==')
	return func

@deco #test = deco(test)
def test():
	print('test函数运行')

test() #== test函数运行

##函数 装饰器 (有参数)
def Typed(**kwargs): 
	def deco(func):
		print('-->>',kwargs)
		return func
	print('->',kwargs)
	return deco

@Typed(x=1,y=2,z=3) #1.Typed(x=1,y=2,z=3) -->>>deco  2.deco -->> test = deco(test)
def test():
	print('test函数运行')

test() #-> {'x': 1, 'y': 2, 'z': 3}

##类 装饰器 无参数
def deco(cls):
	print('====')
	return cls

@deco # Foo = deco(Foo)
class Foo:
	pass

f1 = Foo()
print(f1) #<__main__.Foo object at 0x0000026778CD72E8>

##验证一切皆对象
def deco(cls):
	print('====',cls)  #==== <class '__main__.Foo'>
	cls.x = 1
	cls.y = 2
	cls.z = 3
	return cls

#类
@deco # Foo = deco(Foo)
class Foo:
	pass

print(Foo.__dict__) #存在赋值的属性:'x': 1, 'y': 2, 'z': 3

#函数
@deco #test = deco(test)
def test():
	print('test函数运行')

print(test.__dict__) #test函数同样有__dict__:{'x': 1, 'y': 2, 'z': 3}

##类 装饰器 无参数
def Typed(**kwargs): #外面嵌套一函数,可以添加额外参数来满足不同的需求
	def deco(cls): #单一的属性添加设置满足不了多个类的不同的需求
		print('-->>',kwargs)
		print('类名-->>',cls)
		for key,val in kwargs.items():
			#cls.__dict__[key] = val 报错
			setattr(cls,key,val)
		return cls
	print('-->>',kwargs)
	return deco

@Typed(x=1,y=2,z=3) #1.Typed(x=1,y=2,z=3) -->>>deco  2.deco -->> Foo = deco(Foo)
class Foo:
	pass
#运行结果:-->> {'x': 1, 'y': 2, 'z': 3} -->> {'x': 1, 'y': 2, 'z': 3} 类名-->> <class '__main__.Foo'>
print(Foo.__dict__) #'x': 1, 'y': 2, 'z': 3

@Typed(name='laowang') #@deco -->> Bar = deco(Bar)
class Bar:
	pass
print(Bar.__dict__) #'name': 'laowang'
#######类装饰器解决多种类型限制定义繁琐问题
class Typed:
	def __init__(self,key,expected_type):
		self.key = key
		self.expected_type = expected_type

	def __get__(self,instance,owner):
		print('get方法')
		print('instance参数【%s】' % instance)
		print('owner参数【%s】' % owner)
		return instance.__dict__[self.key]

	def __set__(self,instance,value):
		print('set方法')
		print('instance参数【%s】' % instance)
		print('value参数【%s】' % value)
		#instance.__dict__['name'] = value
		if not isinstance(value,self.expected_type): ###########增加类型限制功能-->>自定义类型限制
			raise TypeError('你传入的类型不是-->>%s' % self.expected_type) #######报错
		instance.__dict__[self.key] = value

	def __delete__(self,instance):
		print('delete方法')
		print('instance参数【%s】' % instance)
		instance.__dict__.pop(self.key)

def deco(**kwargs): #外面嵌套一函数,可以添加额外参数来满足不同的需求
	def wrapper(obj): #单一的属性添加设置满足不了多个类的不同的需求
		for key,val in kwargs.items():
			#obj.__dict__[key] = val 报错
			print('====',key,val)
			setattr(obj,key,Typed(key,val))  #装饰器里面增加描述符
		return obj
	return wrapper

@deco(name=str,age=int,salary=float,gender=str) #@wrapper -->>People = wrapper(People)
class People:
	#name  = Typed('name',str) #t1.__set__() self.__set__()
	#age = Typed('age',int)
	def __init__(self,name,age,salary,gender):
		self.name = name
		self.age = age
		self.salary = salary

print(People.__dict__) #包含属性和对应的Typed对象

p1 = People('laowang',18,1000.0,'male')

描述符总结

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件

property

#回顾property的使用
class Room:
	def __init__(self,name,width,length):
		self.name = name
		self.width = width
		self.length = length

	@property #area = property(area) 类属型
	def area(self): #area = property(area)
		return self.width * self.length

r1 = Room('laowang',1,1)
print(r1.area)

###############自定制property
class Lazyproperty:
	def __init__(self,func):
		print('-->>',func) #-->> <function Room.area at 0x0000027AB34656A8>
		self.func = func
	def __get__(self,instance,owner):
		print('get')
		print(instance)
		print(owner)
		if instance is None:
			return self
		return self.func(instance)  #返回area方法的值

class Room:

	def __init__(self,name,width,length):
		self.name = name
		self.width = width
		self.length = length

	#@property  #area = property(area) 类属型
	@Lazyproperty  #area = Lazyproperty(area) -->>>实际上是实例化Lazyproperty这个类
	def area(self):
		return self.width * self.length
	@property #test = property(test)
	def test(self):
		return '呵呵哒'

#-->>property
#r1 = Room('house',5,5)
#print(r1.area)
#print(Room.__dict__) #存在'area': <property object at 0x000001C4D60B4728>

#-->>Lazyproperty
r1 = Room('house',5,5)
#print(r1.area) #r1.area->Lazyproperty(area)这个实例对象 <__main__.Lazyproperty object at 0x0000029454C0B0F0>
#然而上面并没有实现property的功能
#print(r1.area.func(r1)) #需要传入r1这个实例对象

#将Lazyproperty类修改为描述符,area被Lazyproperty描述符所代理
#print(r1.__dict__) #{'name': 'house', 'width': 5, 'length': 5} 实例里面找不到area属性,去类里面找
#print(Room.__dict__) #'area': <__main__.Lazyproperty object at 0x000002333B574400> 类里面存在area属性
#print(r1.area) #调用描述符,触发get方法:25

#添加静态属性test
#print(Room.test) #静态属性是给实例对象调用的,传入的参数也是实例本身:<property object at 0x0000023409994818>

#print(r1.area) #instance的值是<__main__.Room object at 0x000002B1831E44E0>
#print(Room.area) #instance的值是None。因为没有实例

#增加判断:if instance is None return self
print(Room.test) #<property object at 0x000001720E374818>
print(Room.area) #<__main__.Lazyproperty object at 0x0000020F7D694400>
#类调用属性,会返回对象,作用不大

###############自定制property实现延迟计算功能
class Lazyproperty:
	def __init__(self,func):
		#print('-->>',func) #-->> <function Room.area at 0x0000027AB34656A8>
		self.func = func
	def __get__(self,instance,owner):
		#print('get')
		#print(instance)
		#print(owner)
		if instance is None:
			return self
		res = self.func(instance)  #返回area方法的值
		setattr(instance,self.func.__name__,res)
		return res
	def __set__(self,instance,value):
		pass

class Room:

	def __init__(self,name,width,length):
		self.name = name
		self.width = width
		self.length = length

	@Lazyproperty  #area = Lazyproperty(area)
	def area1(self):
		return self.width * self.length

	@property #test = property(test)
	def area(self):
		return self.width * self.length

r1 = Room('hosue',5,6)
#print(r1.area1) #30
#print(r1.__dict__) #{'name': 'hosue', 'width': 5, 'length': 6, 'area1': 30}
#print(r1.area1) #r1实例对象中的属性字典里面有了area1这个属性,索引不需要再重复运行func函数计算

#加上__set__方法后,Lazyproperty变为数据描述符,优先级高于实例属性,因此又会重复运行func
print(r1.area1)
print(r1.area1)

classmethod

#类无参数
class ClassMethod:
	def __init__(self,func):
		self.func = func

	def __get__(self,instance,owner):#instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身
		def feedback():
			print('++')
			return self.func(owner)
		return feedback

class People:
	name = 'laowang'

	@ClassMethod  #chi_shi = ClassMethod(chi_shi)
	def chi_shi(cls):
		print('shi很好吃吗 %s' % cls.name)

People.chi_shi() #shi很好吃吗 laowang

p1 = People()
p1.chi_shi() #shi很好吃吗 laowang

#类有参数
class ClassMethod:
	def __init__(self,func):
		self.func = func

	def __get__(self,instance,owner):#instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身
		def feedback(*args,**kwargs):
			print('++')
			return self.func(owner,*args,**kwargs)
		return feedback

class People:
	name = 'laowang'

	@ClassMethod  #chi_shi = ClassMethod(chi_shi)
	def chi_shi(cls,msg):
		print('shi很好吃吗 %s' % cls.name,msg)

People.chi_shi('呵呵哒') #shi很好吃吗 laowang 呵呵哒

p1 = People()
p1.chi_shi('呵呵哒') #shi很好吃吗 laowang 呵呵哒

staticmethod

class StaticMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner):#instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身
        def feedback(*args,**kwargs):
            print('++')
            return self.func(*args,**kwargs)
        return feedback

class People:
    @StaticMethod   #chi_shi=StaticMethod(chi_shi)
    def chi_shi(x,y,z):
        print('------>',x,y,z)

People.chi_shi(1,2,3) #------> 1 2 3

p1=People()
p1.chi_shi(4,5,6)  #------> 4 5 6

property补充

一个静态属性property本质就是实现了get,set,delete三种方法

#用法一
class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter #AAA属性赋值操作
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter #AAA属性删除操作
    def AAA(self):
        print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

#用法二
class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
#实例一
class Goods:

    def __init__(self):
        # 原价
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 实际价格 = 原价 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price


obj = Goods()
print(obj.__dict__)
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
print(obj.price)
del obj.price     # 删除商品原价

#实例二 -->>实现类型检测功能
#第一步:
class People:
    def __init__(self,name):
        self.name=name

    @property
    def name(self):
        return self.name

p1=People('alex') 
#property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以你这样写会触发property内置的set,
#抛出异常

#第二步
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.hehe

    @name.setter
    def name(self,value):
        print('set------>')
        self.hehe = value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.hehe

p1=People('laowang') #self.name实际是存放到hehe里
print(p1.name) #laowang
print(p1.__dict__) #{'hehe': 'laowang'}

p1.name='laozhao'
print(p1.__dict__) #{'hehe': 'laozhao'}

del p1.name
print(p1.__dict__) #{}

#第三步:加上类型检查
class People:
    def __init__(self,name):
        self.name=name #实例化就触发property

    @property
    def name(self):
        # return self.name #无限递归
        print('get------>')
        return self.hehe

    @name.setter
    def name(self,value):
        print('set------>')
        if not isinstance(value,str):
            print('--')
            return self.hehe
        self.hehe = value

    @name.deleter
    def name(self):
        print('delete------>')
        del self.hehe

p1=People('laowang') #self.name实际是存放到hehe里
print(p1.__dict__) #{'hehe': 'laowang'}
p1.name = 1 ##TypeError: 必须是字符串类型

其他补充

#################获得当前文件的上上级文件夹名
import os,sys

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
print(BASE_DIR)
sys.path.append(BASE_DIR) #将主文件夹路径添加到环境变量中

元类。。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值