【问题标题】:Can I define an add method for a descriptor in python 3?我可以在 python 3 中为描述符定义 add 方法吗?
【发布时间】:2019-09-11 22:31:33
【问题描述】:

我在这里参考了 python 文档来了解如何在 python 中创建一个控制 getter 和 setter 方法的类属性。 https://docs.python.org/3/howto/descriptor.html#id6

我用这段代码创建了一个描述符属性类:

class PropNumberString(object):
    def __init__(self, initval=None, name='var_int'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        print("in getter")
        return self.val

    def __set__(self, obj, val):
        print("in setter")
        self.val = int(val)

    def __add__(self, val):
        print("adding")
        print("val to add: ", val)
        self.val = int(self.val) + int(val)
        return self

    def __radd__(self, val):
        return self.__add__(val)


class ConnectionTemplate(object):
    tid = PropNumberString(0, "ID")

我也尝试定义一个 add 方法,即使提供了一个字符串,它也总是添加一个整数。这个不行,好像是调用了“__get__”,然后调用了返回整数的add方法,而不是我的add方法。

这里有一个测试来演示:

my_template = ConnectionTemplate()

print("Getting value")
print(my_template.tid)

print("Updating value")
my_template.tid = 100

print("Doing add")
my_template.tid = my_template.tid + "1"

print(my_template.tid)
print("type: ", type(my_template.tid))

输出:

Getting value
in getter
0
Updating value
in setter
Doing add
in getter
Traceback (most recent call last):
  File "connection_template.py", line 53, in <module>
    my_template.tid = my_template.tid + "1"
TypeError: unsupported operand type(s) for +: 'int' and 'str'

有没有好的解决方案?

编辑: 这就是我最终做的,其中包含了 Alex Hall 提供的答案

class StrInt(int):
    def __add__(self, val):
        return StrInt(super().__add__(int(val)))
    __radd__ = __add__

class PropNumberString(object):
    def __init__(self, val=None, name='var_name'):
        self.val = StrInt(val)
        self.name = name

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

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

class ConnectionTemplate(object):
    tid_field = "ID"

    def __init__(self, tid=0):
        self.tid = PropNumberString(tid, self.ansa_tid_field)

    def __getattribute__(self, key):
        # see https://docs.python.org/3/howto/descriptor.html#id5
        "Emulate type_getattro() in Objects/typeobject.c"
        attrib = object.__getattribute__(self, key)
        if hasattr(attrib, '__get__'):
            return attrib.__get__(None, self)
        return attrib

    def __setattr__(self, key, val):
        try:
            attrib = object.__getattribute__(self, key)
        except AttributeError:
            self.__dict__[key] = val
            return
        if hasattr(attrib, '__set__'):
            attrib.__set__(None, val)
            return
        self.__dict__[key] = val

注意: 我使用自定义描述符而不是属性装饰器的动机是我希望我的属性能够包含自定义元数据,即 json 转储的键值。

【问题讨论】:

  • 可以,但对你没有任何好处; my_template.tid 计算结果为 my_template.tid.__get__(...)。您正在尝试将 1 加到 __get__ 的返回值上,而不是描述符本身。
  • 没有好的(如果有的话)方法可以根据调用的上下文修改函数的返回值。
  • 您可以让__get__ 返回一个对象,其类型确实支持添加intstr,但这远比在您打算使用整数时要求用户提供整数要复杂得多除了发生。

标签: python properties overriding descriptor


【解决方案1】:

您不需要为此定义自定义描述符。您只需要tid 返回一个添加您想要的方式的类。最好在设置而不是获取时进行转换。这是一个实现:

class MyInt(int):
    def __add__(self, val):
        return MyInt(super().__add__(int(val)))

    __radd__ = __add__


class ConnectionTemplate(object):
    def __init__(self):
        self._tid = 0

    @property
    def tid(self):
        return self._tid

    @tid.setter
    def tid(self, val):
        self._tid = MyInt(val)

【讨论】:

  • getter 已经返回一个整数(由 setter 创建);问题是 调用者 试图将"1" 添加到返回的1,而不是int
猜你喜欢
  • 1970-01-01
  • 2015-02-05
  • 1970-01-01
  • 1970-01-01
  • 2021-01-15
  • 2017-05-09
  • 2017-03-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多