Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 元类上的拦截运算符查找_Python_Python 3.x_Operators_Metaclass - Fatal编程技术网

Python 元类上的拦截运算符查找

Python 元类上的拦截运算符查找,python,python-3.x,operators,metaclass,Python,Python 3.x,Operators,Metaclass,我有一个类,它需要对每个操作符进行一些处理,比如\uuuuuuuuuuuuuuuuuuuuuu,\uuuuuuuuuuuuuuu等等 我没有在类中创建每个函数,而是使用一个元类定义操作符模块中的每个操作符 import operator class MetaFuncBuilder(type): def __init__(self, *args, **kw): super().__init__(*args, **kw) attr = '__{0}{1}__'

我有一个类,它需要对每个操作符进行一些处理,比如
\uuuuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuu
等等

我没有在类中创建每个函数,而是使用一个元类定义操作符模块中的每个操作符

import operator
class MetaFuncBuilder(type):
    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        attr = '__{0}{1}__'
        for op in (x for x in dir(operator) if not x.startswith('__')):
            oper = getattr(operator, op)

            # ... I have my magic replacement functions here
            # `func` for `__operators__` and `__ioperators__`
            # and `rfunc` for `__roperators__`

            setattr(self, attr.format('', op), func)
            setattr(self, attr.format('r', op), rfunc)
这种方法工作得很好,但我认为如果只在需要时生成替换操作符会更好

运算符的查找应该在元类上,因为
x+1
是作为
类型(x)来完成的。
x,1)而不是
x。
x,1),但它不会被
\uuuuuuuu getattr\uuuuu
\uuuuuu getattribute\uuu
方法捕获

这是行不通的:

class Meta(type):
     def __getattr__(self, name):
          if name in ['__add__', '__sub__', '__mul__', ...]:
               func = lambda:... #generate magic function
               return func
此外,生成的“函数”必须是绑定到所用实例的方法

关于如何拦截此查找有什么想法吗?我不知道我想做什么是否很清楚


对于那些质疑我为什么需要这样做的人,请检查完整的代码。 这是一个生成函数的工具(只是为了好玩),可以替代
lambda
s

例如:

>>> f = FuncBuilder()
>>> g = f ** 2
>>> g(10)
100
>>> g
<var [('pow', 2)]>
>>f=FuncBuilder()
>>>g=f**2
>>>g(10)
100
>>>g

作为记录,我不想知道另一种方法来做同样的事情(我不会声明类中的每一个操作符…这会很无聊,而且我的方法工作得很好:)。我想知道如何从操作员那里截取属性查找。

看起来你把事情弄得太复杂了。您可以定义一个mixin类并从中继承。这既比使用元类简单,又比使用
\uuu getattr\uuu
运行得更快

class OperatorMixin(object):
    def __add__(self, other):
        return func(self, other)
    def __radd__(self, other):
        return rfunc(self, other)
    ... other operators defined too
然后,您想要拥有这些运算符的每个类都从OperatorMixin继承

class Expression(OperatorMixin):
    ... the regular methods for your class

在需要时生成运算符方法不是一个好主意:
\uuu getattr\uuu
与常规方法查找相比速度较慢,而且由于方法只存储一次(在mixin类上),因此几乎不节省任何东西。

一些黑魔法让您实现您的目标:

operators = ["add", "mul"]

class OperatorHackiness(object):
  """
  Use this base class if you want your object
  to intercept __add__, __iadd__, __radd__, __mul__ etc.
  using __getattr__.
  __getattr__ will called at most _once_ during the
  lifetime of the object, as the result is cached!
  """

  def __init__(self):
    # create a instance-local base class which we can
    # manipulate to our needs
    self.__class__ = self.meta = type('tmp', (self.__class__,), {})


# add operator methods dynamically, because we are damn lazy.
# This loop is however only called once in the whole program
# (when the module is loaded)
def create_operator(name):
  def dynamic_operator(self, *args):
    # call getattr to allow interception
    # by user
    func = self.__getattr__(name)
    # save the result in the temporary
    # base class to avoid calling getattr twice
    setattr(self.meta, name, func)
    # use provided function to calculate result
    return func(self, *args)
  return dynamic_operator

for op in operators:
  for name in ["__%s__" % op, "__r%s__" % op, "__i%s__" % op]:
    setattr(OperatorHackiness, name, create_operator(name))


# Example user class
class Test(OperatorHackiness):
  def __init__(self, x):
    super(Test, self).__init__()
    self.x = x

  def __getattr__(self, attr):
    print "__getattr__(%s)" % attr
    if attr == "__add__":
      return lambda a, b: a.x + b.x
    elif attr == "__iadd__":
      def iadd(self, other):
        self.x += other.x
        return self
      return iadd
    elif attr == "__mul__":
      return lambda a, b: a.x * b.x
    else:
      raise AttributeError

## Some test code:

a = Test(3)
b = Test(4)

# let's test addition
print(a + b) # this first call to __add__ will trigger
            # a __getattr__ call
print(a + b) # this second call will not!

# same for multiplication
print(a * b)
print(a * b)

# inplace addition (getattr is also only called once)
a += b
a += b
print(a.x) # yay!
输出

__getattr__(__add__)
7
7
__getattr__(__mul__)
12
12
__getattr__(__iadd__)
11
现在,您可以通过继承my
OperatorHackness
基类来使用第二个代码示例。您甚至还可以获得一个额外的好处:
\uuuu getattr\uuuu
对于每个实例和操作符只调用一次,并且缓存不涉及额外的递归层。因此,我们避免了方法调用比方法查找慢的问题(Paul Hankin正确地注意到了这一点)


注意:添加操作符方法的循环在整个程序中只执行一次,因此准备工作需要毫秒范围内的恒定开销。

当前的问题是Python在对象的类上查找
\uuuuxxx\uucode>方法,而不是在对象本身上——如果找不到它,它不会返回到
\uuuu getattr\uuuu
\uuuu getattribute\uuuu

拦截此类调用的唯一方法是已有一个方法。它可以是一个存根函数,就像Niklas Baumstark的回答一样,也可以是一个成熟的替换函数;但是,无论哪种方式,都必须存在某些内容,否则您将无法拦截此类呼叫


如果您仔细阅读,您会注意到将最终方法绑定到实例的要求不是一个可能的解决方案——您可以这样做,但Python永远不会调用它,因为Python正在查看实例类,而不是实例,例如
\uuuuxxx\uuuu
方法。Niklas Baumstark为每个实例创建一个唯一的temp类的解决方案是尽可能接近该要求的。

是的,至少有10个操作符(加上就地和反向形式),我不想手工编写它们并调用完全相同的函数(更改操作符)我现在的想法是,当操作符被调用时,只创建
func
rfunc
。懒散地创建函数会给你带来什么?这是因为类是一个函数生成器。它被设计成只需很少的时间就可以使用,结果对象是一个可以根据用户需要多次调用的对象。在这种情况下,我会创建一个充满函数的类,比如“make_adder”等。不要重写任何东西。然后,为您需要的每个特定类创建基类的子类。“我有一个类,它需要对每个操作符进行一些处理”-为什么?听起来你像是在剥一棵非常复杂的树…@Lennartreegebro我正在用某个对象上的运算符编写一个函数生成器<代码>f=FuncBuilder();g=f**2+1;g(10)=101
。它不是很有用(很多函数调用),但使用起来有些有趣:D@LennartRegebro我发布了完整的代码。好的,您已经创建了一种制作lambdas的方法。:-)只要你只是为了好玩,一切都没关系。:-)看起来你的
for
循环正在向类中添加所有运算符(看看我的代码,我也这么做了)。我不想要它们:)。顺便说一句,我认为这已经是一个进步。@JBernardo:再看看。它的工作原理与您的代码完全不同。添加的不是已创建的运算符函数,而是围绕
\uu getattr\uu
调用的浅包装。这是必要的,因为正如您所说的,您无法使用自定义的
\uu getattr\uu
函数拦截这些方法调用。由于循环在整个程序中只执行一次,并且运算符的数量是有限的,因此它需要毫秒范围内的恒定开销。基本上,这是一个黑客程序,允许你使用
\uuu getattr\uuuuuuuuuuuuuuuu>像其他方法一样拦截操作符(这正是你所要求的)。我理解你的代码(你也应该在答案中添加这些注释),但你要做的是:
x+y->x.\uu添加\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。这是一个有趣的想法,但似乎没有操作符是不可能的。它更像是
x+y->x.\uu添加\uu->cached\u func=x.\uu getattr\uuuuuuu(“\uu添加\uuuuuu”)
。第二次