gpt4 book ai didi

python - 如何装饰类并使用描述符访问属性?

转载 作者:行者123 更新时间:2023-11-30 22:37:38 25 4
gpt4 key购买 nike

我正在尝试掌握(开始;))以了解如何在 Python 3 上正确使用装饰器和描述符。我想出了一个想法,我正在尝试弄清楚如何对其进行编码。

我希望能够创建一个用某些“函数”B或“类”B装饰的类A,它允许我创建 A 的实例,将 A 的属性声明为某种类型的组件并为 A 赋值 __init__魔术函数。例如:

组件化是某些“函数B”或“类B”,它允许我声明一个类Vector。我声明 xy 是一个组件( float ),如下所示:

@componentized 
class Vector:
x = component(float)
y = component(float)
def __init__ (self, x, y):
self.x = x
self.y = y

我的想法是能够做到这一点:

v = Vector(1,2)
v.x #returns 1

但主要目标是我想对每个标记的组件( float )属性执行此操作:

v.xy #returns a tuple (1,2)
v.xy = (3,4) #assigns to x the value 3 and y the value 4

我的想法是创建一个装饰器@componentized来覆盖__getattr____setattr__魔术方法。类似这样:

def componentized(cls):
class Wrapper(object):
def __init__(self, *args):
self.wrapped = cls(*args)

def __getattr__(self, name):
print("Getting :", name)

if(len(name) == 1):
return getattr(self.wrapped, name)

t = []
for x in name:
t.append(getattr(self.wrapped, x))

return tuple(t)

@componentized
class Vector(object):
def __init__(self, x, y):
self.x = x
self.y = y

这确实有效,但我认为我不太明白发生了什么。因为当我尝试进行分配并覆盖 __setattr__ 魔术方法时,即使我实例化该类,它也会被调用。在以下示例中两次:

vector = Vector(1,2)
vector.x = 1

我怎样才能实现这种行为?

提前致谢!如果需要更多信息,请随时询问!

编辑:

按照@Diego的回答,我设法做到了这一点:

def componentized(cls):
class wrappedClass(object):
def __init__(self, *args, **kwargs):
t = cls(*args,**kwargs)
self.wrappedInstance = t

def __getattr__(self, item):
if(len(item) == 1):
return self.wrappedInstance.__getattribute__(item)
else:
return tuple(self.wrappedInstance.__getattribute__(char) for char in item)

def __setattr__(self, attributeName, value):
if isinstance(value, tuple):
for char, val in zip(attributeName, value):
self.wrappedInstance.__setattr__(char, val)
elif isinstance(value, int): #EMPHASIS HERE
for char in attributeName:
self.wrappedInstance.__setattr__(char, value)
else:
object.__setattr__(self, attributeName, value)
return wrappedClass

并且有一个像这样的Vector类:

@componentized 
class Vector:
def __init__ (self, x, y):
self.x = x
self.y = y

这有点像我想要的,但我仍然不知道如何实现:

x = component(float)
y = component(float)

Vector类中以某种方式订阅float类型的xy,所以当我执行#EMPHASIS LINE(在我硬编码特定类型的行中)我可以检查代码中是否有人正在为实例的 x 和/或 y 分配值Vector 的类型是我定义的:

x = component(float)

所以我尝试了这个(组件(描述符)类):

class component(object):
def __init__(self, t, initval=None):
self.val = initval
self.type = t

def __get__(self, obj, objtype):
return self.val

def __set__(self, obj, val):
self.val = val

像描述符一样使用组件,但我无法设法采取解决方法来处理该类型。我尝试创建一个数组来保存 valtype,但后来不知道如何从装饰器 __setattr__ 方法中获取它。

你能指出我正确的方向吗?

PS:希望你们理解我想要做什么并帮助我。提前致谢

解决方案

好吧,使用@Diego的答案(我将接受)和一些解决方法来满足我的个人需求,我成功做到了:

装饰器(组件化)

def componentized(cls):
class wrappedClass(object):
def __init__(self, *args):
self.wrappedInstance = cls(*args)

def __getattr__(self, name):
#Checking if we only request for a single char named value
#and return the value using getattr() for the wrappedInstance instance
#If not, then we return a tuple getting every wrappedInstance attribute
if(len(name) == 1):
return getattr(self.wrappedInstance, name)
else:
return tuple(getattr(self.wrappedInstance, char) for char in name)

def __setattr__(self, attributeName, value):
try:
#We check if there is not an instance created on the wrappedClass __dict__
#Meaning we are initializing the class
if len(self.__dict__) == 0:
self.__dict__[attributeName] = value
elif isinstance(value, tuple): # We get a Tuple assign
self.__checkMultipleAssign(attributeName)
for char, val in zip(attributeName, value):
setattr(self.wrappedInstance, char, val)
else:
#We get a value assign to every component
self.__checkMultipleAssign(attributeName)
for char in attributeName:
setattr(self.wrappedInstance, char, value)
except Exception as e:
print(e)

def __checkMultipleAssign(self, attributeName):
#With this we avoid assigning multiple values to the same property like this
# instance.xx = (2,3) => Exception
for i in range(0,len(attributeName)):
for j in range(i+1,len(attributeName)):
if attributeName[i] == attributeName[j]:
raise Exception("Multiple component assignment not allowed")
return wrappedClass

组件(描述符类)

class component(object):
def __init__(self, t):
self.type = t #We store the type
self.value = None #We set an initial value to None

def __get__(self, obj, objtype):
return self.value #Return the value

def __set__(self, obj, value):
try:
#We check whether the type of the component is diferent to the assigned value type and raise an exeption
if self.type != type(value):
raise Exception("Type \"{}\" do not match \"{}\".\n\t--Assignation never happened".format(type(value), self.type))
except Exception as e:
print(e)
else:
#If the type match we set the value
self.value = value

(代码注释不言自明)

通过这个设计,我可以实现我想要的(如上所述)感谢大家的帮助。

最佳答案

我认为有一个最简单的方法来实现这种行为:重载 __getattr____setattr__功能。

获取向量.xy:

class Vector:
...

def __getattr__(self, item):
return tuple(object.__getattribute__(self, char) for char in item)

__getattr__仅当访问属性的“正常”方式失败时才调用函数,如 Python documentation 中所述。 。所以,当python找不到vector.xy时,__getattr__方法被调用,我们返回每个值的元组(即 x 和 y)。
我们使用object.__getattribute__以避免无限递归。

设置向量.abc:

    def __setattr__(self, key, value):

if isinstance(value, tuple) and len(key) == len(value):
for char, val in zip(key, value):
object.__setattr__(self, char, val)

else:
object.__setattr__(self, key, value)

__setattr____getattr__ 不同,方法总是被调用,因此仅当我们要设置的项与值元组的长度相同时,我们才单独设置每个值。

>>> vector = Vector(4, 2)
>>> vector.x
4
>>> vector.xy
(4, 2)
>>> vector.xyz = 1, 2, 3
>>> vector.xyxyxyzzz
(1, 2, 1, 2, 1, 2, 3, 3, 3)

唯一的缺点是,如果您确实想分配一个元组(假设您有一个名为 size 的属性):

vector.size = (1, 2, 3, 4)

那么 s、i、z 和 e 将被分别赋值,这显然不是你想要的!

关于python - 如何装饰类并使用描述符访问属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43827908/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com