gpt4 book ai didi

python - 将描述符方法的实例参数绑定(bind)到调用对象实例

转载 作者:行者123 更新时间:2023-11-30 23:16:52 25 4
gpt4 key购买 nike

在描述符中,__get__ 的第二个参数和 __set__绑定(bind)到调用对象实例(__get__ 的第三个参数绑定(bind)到调用所有者类对象):

class Desc():
def __get__(self,instance,owner):
print("I was called by",str(instance),"and am owned by",str(owner))
return self

class Test():
desc = Desc()

t = Test()
t.desc

我将如何创建一个装饰器以将另一个描述符方法的第二个参数( __get____set____delete__ 除外)绑定(bind)到实例对象?

示例(只是一个示例;不是我真正想做的事情):
class Length(object):
'''Descriptor used to manage a basic unit system for length'''
conversion = {'inches':1,'centimeters':2.54,'feet':1/12,'meters':2.54/100}
def __set__(self,instance,length):
'''length argument is a tuple of (magnitude,unit)'''
instance.__value = length[0]
instance.__units = length[1]
def __get__(self,instance,owner):
return self
@MagicalDecoratorOfTruth
def get_in(self, instance, unit): #second argument is bound to instance object
'''Returns the value converted to the requested units'''
return instance.__value * (self.conversion[units] / self.conversion[instance.__units])

class Circle(object):
diameter = Length()
def __init__(self,diameter,units):
Circle.diameter.__set__((diameter,units))

c = Circle(12,'inches')
assert c.diameter.get_in('feet') == 1
c.diameter = (1,'meters')
assert c.diameter.get_in('centimeters') == 100

我考虑尝试的一种方法是包装 get_in带有装饰器的方法。使用 @classmethod 装饰器可以完成类似的操作,其中类方法的第一个参数绑定(bind)到类对象而不是类实例对象:
class Test():
@classmethod
def myclassmethod(klass):
pass

t = Test()
t.myclassmethod()

但是,我不确定如何将其应用于上述情况。

避免整个问题的一种方法是将实例对象显式传递给描述符方法:
c = Circle(12,'inches')
assert c.diameter.get_in(c,'feet') == 1
c.diameter = (1,'meters')
assert c.diameter.get_in(c,'centimeters') == 100

但是,这似乎违反了 D.R.Y.,而且很难启动。

最佳答案

Descriptor 协议(protocol)中为这类事情留下了一个钩子(Hook)——即,当从类级别访问 Descriptor 对象时,instance 的值将是 None .

从相反的方向考虑这一点很有用。让我们从 Circle 开始:

class Circle(object):
diameter = Length()
def __init__(self, diameter, units):
self.diameter = (diameter, units)

请注意,与其尝试手动调用 __set__或从类级别调用事物(例如,通过直接从 Circle 调用)——我只是按预期使用描述符,只需设置一个值。

现在,对于描述符,几乎所有内容都是一样的。我清理了转换的代码样式 dict .

但是对于 __get__每当 instance == None 时,我都会添加额外的检查。 .每当 Circle.diameter 时都会出现这种情况。被访问,而不是 c.diameter对于一些 c这是 Circle 的一个实例.确保您对差异感到满意。
class Length(object):
conversion = {'inches':1.0,
'centimeters':2.54,
'feet':1.0/12,
'meters':2.54/100}

def __set__(self, instance, length):
instance.__value = length[0]
instance.__units = length[1]

def __get__(self, instance, owner):
if instance is None:
return self
return (instance.__value, instance.__units)

def get_in(self, instance, units):
c_factor = self.conversion[units] / self.conversion[instance.__units]
return (c_factor * instance.__value, units)

现在,我们可以得到实际的 Length.diameter 内部的实例...但前提是我们访问 .diameter挂断 Circle (类本身),而不是该类的任何实例。
# This works and prints the conversion for `c`.
c = Circle(12, 'inches')
Circle.diameter.get_in(c, 'feet')

# This won't work because you short-circuit as soon as you type `c.diameter`
c.diameter.get_in('feet')

避免需要离开实例的一种选择是猴子补丁使用 __class__ 的函数。属性:
class Circle(object):
diameter = Length()
def __init__(self, diameter, units):
self.diameter = (diameter, units)
self.convert = lambda attr, units: (
getattr(self.__class__, attr).get_in(self, units)
)

现在实例 c可以这样工作:
>>> c.convert('diameter', 'feet')
(1.0, 'feet')

您可以改为定义 convert作为实例方法(例如,使用通常的 self 第一个参数),或者您可以使用装饰器或元类等来实现。

但归根结底,您仍然需要非常小心。从表面上看,这看起来很有吸引力,但实际上您在对象之间添加了很多耦合。从表面上看,您似乎将单位转换的关注点与对象对“成为一个圆”的关注点分开了——但实际上,您正在增加其他程序员必须解决的复杂层。而且你正在将你的类(class)与这个特定的描述符结合起来。如果有人在重构中确定直径转换作为一个完全在 Circle 之外的函数更好。对象,他们现在突然不得不担心准确计算 Length 的所有事件部分。当他们进行重构时。

归根结底,您还必须问这会给您带来什么。据我所知,在您的示例中,除了能够将转换计算作为所谓的“流畅界面”设计风格的一部分的非常小的便利性之外,它什么也不买……例如副作用和函数调用看起来只是属性访问。

就个人而言,我不喜欢这种语法。我更愿意使用类似的风格
convert(c.diameter, 'feet')


Circle.diameter.convert('feet')

像第一个版本这样的功能通常存在于模块级别,它们可以概括为它们将要操作的类型。可以扩展它们以更轻松地处理新类型(如果您希望对函数进行继承,可以将它们封装到它们自己单独的类中)。通常,它们也更容易测试,因为调用它们所需的机器要少得多,并且测试模拟对象可以更简单。事实上,在像 Python 这样的动态类型语言中,允许像 convert 这样的函数。基于鸭子类型的工作通常是该语言的主要优点之一。

这并不是说一种方式肯定比另一种更好。一个好的设计师可以在这两种方法中找到优点。一个糟糕的设计师可能会把这两种方法弄得一团糟。但总的来说,我发现当 Python 的这些特殊的角落被用来解决普通的、常规的问题时,往往会导致困惑。

关于python - 将描述符方法的实例参数绑定(bind)到调用对象实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27574498/

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