【问题标题】:How to make a proxy object behave like the integer it wraps?如何使代理对象表现得像它包装的整数?
【发布时间】:2016-08-27 23:27:34
【问题描述】:

我想创建一个代理类来包装int 以进行线程安全访问。与内置类型相比,代理类是可变的,因此它可以就地递增。现在,我想将该类用作外部的普通整数。通常,Python 的__getattr__ 可以很容易地将属性访问转发到内部对象:

class Proxy:

    def __init__(self, initial=0):
        self._lock = threading.Lock()
        self._value = initial

    def increment(self):
        with self._lock:
            self._value += 1

    def __getattr__(self, name):
        return getattr(self._value, name)

但是,__getattr__ does not get triggered for magic methods__add____rtruediv__ 等,我需要代理像整数一样工作。有没有办法自动生成这些方法,或者将它们转发给包装的整数对象?

【问题讨论】:

  • 官方文档中也提到了隐式查找:docs.python.org/3/reference/datamodel.html#special-lookup
  • @IljaEverilä 我知道为什么__getattr__ 对此不起作用,这不是我的问题要问的。我正在寻求一种生成或以其他方式转发方法的方法。因此,如果您想以这种方式查看,则可以使用一种解决方法。
  • @VPfB。链接似乎死了。你有更新的版本,或者至少有帖子的标题吗?
  • @MadPhysicist 不幸的是,没有,但我找到了保存的副本。链接是http://web.archive.org/web/20160516220457/http://yauhen.yakimovich.info/blog/2011/08/12/wrapping-built-in-python-types/

标签: python python-3.x proxy


【解决方案1】:

comments 中的@VPfB 链接的博客文章有一个更通用和更彻底的解决方案来代理内置类型的 dunder 方法,但这里有一个简化且相当粗暴的示例。我希望它有助于理解如何创建这样的转发方法。

import threading
import numbers


def _proxy_slotted(name):
    def _proxy_method(self, *args, **kwgs):
        return getattr(self._value, name)(*args, **kwgs)
    # Not a proper qualname, but oh well
    _proxy_method.__name__ = _proxy_method.__qualname__ = name
    return _proxy_method

# The list of abstract methods of numbers.Integral
_integral_methods = """
    __abs__ __add__ __and__ __ceil__ __eq__ __floor__
    __floordiv__ __int__ __invert__ __le__ __lshift__
    __lt__ __mod__ __mul__ __neg__ __or__ __pos__ __pow__
    __radd__ __rand__ __rfloordiv__ __rlshift__ __rmod__
    __rmul__ __ror__ __round__ __rpow__ __rrshift__
    __rshift__ __rtruediv__ __rxor__ __truediv__ __trunc__
    __xor__""".split()

# The dunder, aka magic methods
_Magic = type('_Magic', (),
              {name: _proxy_slotted(name)
               for name in _integral_methods})


class IntProxy(_Magic, numbers.Integral):
    """
    >>> res = IntProxy(1) + 1
    >>> print(type(res), res)
    <class 'int'> 2
    >>> print(IntProxy(2) / 3)
    0.6666666666666666
    """

    def __init__(self, initial=0, Lock=threading.Lock):
        self._lock = Lock()
        self._value = initial

    def increment(self):
        with self._lock:
            self._value += 1

    def __getattr__(self, name):
        return getattr(self._value, name)

【讨论】:

    猜你喜欢
    • 2020-04-17
    • 1970-01-01
    • 2010-10-27
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 2018-12-13
    • 2015-02-08
    • 1970-01-01
    相关资源
    最近更新 更多