大家好,小编来为大家解答以下问题,python语言的描述错误的选项,python描述算法的方法有几种,今天让我们一起来看看吧!
一、描述符是什么
描述符:是一个类,只要内部定义了方法__get__, __set__, __delete__中的一个或者多个。描述符,属性,方法绑定等内部机制都是描述符在起作用python打印皮卡丘怎么弄。描述符以单个属性出现,并针对该属性的不同访问行为做出响应。最重要的是,描述符能“感知”通过什么引用该属性,从而和目标建立绑定关联。
二、描述符的实现
class Deor:
"""
描述符
"""
def __set_name__(self, owner, name):
"""
描述符属性必须定义为类型成员,所以其自身不适合存储实例相关的状态,在创建属性时,__set_name__方法被调用,并可以通过参数获知目标类型(owner),以及属性名称
:param owner:
:param name:
:return:
"""
print(self, owner, name)
self.name = f"__{name}__"
def __get__(self, instance, owner):
"""
以类型或实例访问描述符属性时,__get__被自动调用,且会接收到类型和实例引用
:param instance:
:param owner:
:return:
"""
print(self,instance,owner)
return getattr(instance,self.name, None)
def __set__(self, instance, value):
"""
仅在实例引用时被调用。以类型引用进行赋值,会导致描述符属性被替换
:param instance:
:param value:
:return:
"""
print(self, instance, value)
setattr(instance, self.name, value)
def __delete__(self, instance):
"""
仅在实例被引用时调用。以类型引用进行删除操作,会导致描述符属性被删除
:param instance:
:return:
"""
print(self, instance)
raise AttributeError("delete is disabled")
class X:
data = Deor()
x = X() # 执行__set_name__ <__main__.deor object at> data
x.data = 100 # 执行__set__ <__main__.deor object at> <__main__.x object at> 100
print(x.data) # 执行__get__ <__main__.deor object at> <__main__.x object at>
print(x.__dict__) # {'__data__': 100}
print(X.__dict__) # {'__module__': '__main__', 'data': <__main__.deor object at>, '__dict__': , '__weakref__': , '__doc__': None}
X.data = 2 # 以类型引用进行赋值,会导致描述符属性被替换
print(x.data) # 2
print(X.data) # 2
print(x.__dict__) # {'__data__': 100}
print(X.__dict__) # {'__module__': '__main__', 'data': 2, '__dict__': , '__weakref__': , '__doc__': None}
三、数据描述符
如果定义了__set__或__delete__方法,那么我们便称其为数据描述符(data deor),而仅有__get__的则是非数据描述符(non-data deor)。这两者的区别在于,数据描述符属性的优先级高于实例名字空间中的同名成员。
class Deors:
"""
数据描述符
"""
def __set_name__(self, owner, name):
self.name = name # 获取Deors 实例对象名字
def __get__(self, instance, owner):
print("执行Deors的get")
return self.name
def __set__(self, instance, value):
self.name = value
print("执行Deors的set")
def __delete__(self, instance):
print("执行Deors的delete")
class Light:
# 使用描述符
name = Deors()
def __init__(self, name, price):
self.name = name
self.price = price
# 使用类的实例对象来测试
light = Light("电灯泡", 60) # 执行描述符的set内置属性
light.name # 执行描述符的get内置属性
print(light.__dict__) # 查看实例的字典,不存在name {'price': 60}
print(Light.__dict__) # 查看类的字典,存在name(为描述符的对象)
# {'__module__': '__main__', 'name': <__main__.deors object at>,
# '__init__': , '__dict__': ,
# '__weakref__': , '__doc__': None}
del light.name # 执行描述符的delete内置属性
del Light.name # 以类型引用进行删除操作,会导致描述符属性被删除
print(Light.__dict__) # {'__module__': '__main__', '__init__': , '__dict__': , '__weakref__': , '__doc__': None}
print(light.name) # 报错,描述符属性被删除
如果注释掉__set__,就成为了非数据描述符。
描述符的优先级问题:类属性>数据描述符>实例属性>非数据描述符>找不到属性触发__getattr__()
说明问题一:类属性>数据描述符
class Deor:
def __get__(self, instance, owner):
print("__get__")
return self.name
def __set__(self, instance, value):
print("开始赋值:", value)
self.name = value
print("__set__")
class X:
data = Deor()
x = X()
x.data = 100 # 调用__set__赋值
print(x.__dict__) # {}
x.data = 3
print(x.data) # 3
print(x.__dict__) # {}
print(X.data) # 调用__get__
print(X.__dict__) # {'__module__': '__main__', 'data': <__main__.deor object at>, '__dict__': , '__weakref__': , '__doc__': None}
X.data = 44444 # 语句没有触发set的执行,说明类属性的优先级大于数据描述符的优先,此时相当于类属性覆盖了数据描述符,从而说明对类属性的一切操作都与描述符无关
print(x.data) # 4
print(x.__dict__) # {}
print(X.__dict__) # {'__module__': '__main__', 'data': 44444, '__dict__': , '__weakref__': , '__doc__': None}
说明问题二:数据描述符>实例属性 参考“描述符代码” ,数据描述符的优先级大于实例属性的优先级,此时实例属性name被数据描述符所覆盖,而price没有描述符代理,所以它任然是实例属性。
说明问题三:实例属性>非数据描述符
class Deors:
"""
非数据描述符
"""
def __get__(self, instance, owner):
print("执行Deors的get")
def __delete__(self, instance):
print("执行Deors的delete")
class X:
data = Deors()
x = X()
x.data = 3 # 报错,AttributeError: __set__
四、方法绑定
因为函数默认实现了描述符协议,所以当以实例或类型访问方法时,__get__首先被调用。类型和实例作为参数被传入__get__,从而截获绑定目标(self),如此就将函数包装称绑定方法对象返回。实际被执行的,就是这个会隐式传入第一个参数的包装品。
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def print_info(self):
print("my name is %s ,my age is %s" % (self.name, self.age))
p = Person("ways", 13)
print(p.print_info) # >
print(p.print_info.__get__(p,Person)) # >
m = p.print_info.__get__(p,Person)
Person.print_info(m.__self__,) # my name is ways ,my age is 13
print(m.__self__, m.__func__) # <__main__.person object at>
"""
方法执行分成了两个步骤:
p.print_info():
#1. m = p.print_info.__get__(p,Person) 将函数包装成绑定方法
#2. m()等价Person.print_info(m.__self__,) 执行时,隐式将self/cls参数传递给目标函数
"""
五、描述符的使用例子
1、模拟property
class My_Property:
"""
使用描述符模拟property
"""
def __init__(self, func):
self.func = func
print(self.func) #
def __get__(self, instance, owner):
res = self.func(instance) # 回调传入的函数,将运行结果保存在res中
setattr(instance,self.func.__name__,res) # 为函数名func.__name__ 设置值为res,存入对象的字典
return res
class Test:
def __init__(self,weight,height):
self.weight = weight
self.height = height
@My_Property
def my_area(self):
return self.weight*self.height
test = Test(3, 4)
print(test.my_area) # 12
print(test.__dict__) # {'weight': 3, 'height': 4, 'my_area': 12}
六、描述符的使用总结
1、描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性
2、描述父是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件.
参考:https://www.cnblogs.com/Lynnblog/p/9033455.html 和《python3学习笔记上》