简体   繁体   English

Python - 作为类属性的函数成为绑定方法

[英]Python - function as class attribute becomes a bound method

I noticed that if I define a class attribute equal to a function when I create an instance of that class the attribute becomes a bound method.我注意到,如果我在创建该类的实例时定义了一个与函数相等的类属性,则该属性将成为绑定方法。 Can someone explain me the reason of this behaviour?有人可以向我解释这种行为的原因吗?

In [9]: def func():
   ...:     pass
   ...: 

In [10]: class A(object):
   ....:     f = func
   ....:     

In [11]: a = A()

In [12]: a.f
Out[12]: <bound method A.func of <__main__.A object at 0x104add190>>

In [13]: a.f()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-19134f1ad9a8> in <module>()
----> 1 a.f()
    global a.f = <bound method A.func of <__main__.A object at 0x104add190>>

TypeError: func() takes no arguments (1 given)

You assigned a function to the attribute Af (the attribute f of the class A ).您为属性Af (类A的属性f )分配了一个函数。 The attribute Af was defined as part of the class.属性Af被定义为类的一部分。 It is a function, so it is by default an instance method of that class.它是一个函数,因此默认情况下它是该类的实例方法

Creating an instance (named a ) of class A causes that instance to have an attribute f , and you access that by the name af .创建类A的实例(名为a )会导致该实例具有属性f ,您可以通过名称af访问该属性。 This is a bound method (cause it's bounded to the object a ; further explanation here ).这是一个绑定方法(因为它绑定到对象a这里a 进一步的解释)。

Every instance method, when it is called, automatically receives the instance as its first argument (conventionally named self ).每个实例方法在调用时都会自动接收实例作为其第一个参数(通常命名为self )。 Other types of method are possible: - see class methods and static methods .其他类型的方法也是可能的: - 请参阅类方法和静态方法

For this reason the error says that func takes no arguments (as it's defined as def func(): ) but received 1 ( self ).出于这个原因,错误说func没有参数(因为它被定义为def func(): )但收到了 1 ( self )。

To do what you want, you should tell python that you're using a static method要做你想做的事,你应该告诉 python 你正在使用 静态方法

def func():
    pass

class A(object):
    f = staticmethod(func)

Python is not a message based OO system 1 . Python不是基于消息的 OO 系统1 Instead, similar to JavaScript, properties are resolved to first-class functions and then invoked;相反,类似于 JavaScript,属性被解析为一等函数,然后被调用; the behavior differs a bit in the mechanics of such, as discovered.正如发现的那样,这种行为在机制上略有不同。

In Python the requirement is that methods have at least one parameter, normally called self , that will be automatically supplied the associated instance when it is invoked as a method.在 Python 中,要求方法至少有一个参数,通常称为self它作为方法调用将自动提供关联的实例。

Furthermore (and perhaps to the point of the question), Python does not differentiate between using def f.. or f = some_func() when establishing instance member bindings;此外(也许是问题的关键),Python 在建立实例成员绑定时不区分使用def f..f = some_func() arguably this matches behavior outside of classes.可以说这与类之外的行为相匹配。

In the example, assigning the function to the instance 'makes it expect to be treated like an instance method'.在示例中,将函数分配给实例“使其期望被视为实例方法”。 It is the exact same - parameterless - function called in both cases;它是完全相同的 - 无参数 - 在两种情况下调用的函数; only the future usage of such is relevant.只有此类的未来使用才是相关的。

Now, unlike JavaScript, Python handles methods and object association through the concept of bound methods - functions resolved as methods are always 'bound'.现在,与 JavaScript 不同,Python 通过绑定方法的概念来处理方法和对象关联 - 解析为方法的函数总是“绑定”的。

The behavior of af returning a bound method - function that will automatically supply the bound object to the first parameter as self - is done independently of the source of the function. af返回绑定方法的行为 - 将自动将绑定对象作为self提供给第一个参数的函数 - 独立于函数的源完成。 In this case that means the parameterless function cannot be used when it is 'bound' as it does not accept a self parameter.在这种情况下,这意味着无参数函数在“绑定”时不能使用,因为它不接受self参数。

As a demonstration, the following will fail in the same way because the source underlying method does not meet the minimum requirements of accepting the instance as an argument:作为示范,下面将用同样的方法失败,因为该源底层方法符合接受实例作为参数的最低要求:

g = a.f
g()

In this case calling g() is equivalent to calling func(a) .在这种情况下,调用g()等效于调用func(a)


1 For comparison, Java, C#, Ruby, and SmallTalk are message based OO systems - in these an object is told to invoke a method by a 'name', instead of resolving a method (or function) as a value which can be invoked. 1相比之下,Java、C#、Ruby 和 SmallTalk 是基于消息的 OO 系统 - 在这些系统中,对象被告知通过“名称”调用方法,而不是将方法(或函数)解析为可以调用的值.

A little late to the party, but another viable solution is in storing the function as dictionary, which is an attribute of the class.聚会有点晚了,但另一个可行的解决方案是将函数存储为字典,这是类的一个属性。

# Some arbitrary function
def f(x):
  return x

# A class, which will wrap the function as a dictionary object, which is a class attr
class Test:
  def __init__(self,f):
    self.f = {'f':f}
  
  def func(self,x):
    return self.f['f'](x)

# Now let's test the Test object
t = Test(f=f)
t.func(3)

>>>
3

This approach is more verbose than the accepted answer, however, it doesn't get into staticmethod, decorators, or other advanced topics.这种方法比公认的答案更冗长,但是,它没有涉及静态方法、装饰器或其他高级主题。 So, this will work in a pinch if you're intimidated by the other, preferred approach.因此,如果您被另一种首选方法吓倒,这将在紧要关头起作用。

Edit: If you want this to robust enough to handle keyword arguments:编辑:如果您希望它足够强大以处理关键字参数:

class Test:
  def __init__(self,f):
    self.f = {'f':f}
  
  def func(self,p):
    return self.f['f'](**p)

def f(**p):
  return p['x'] * p['y']

t = Test(f=f)
t.func({'x':2,'y':3})

>>>
6

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM