作为类属性的函数赋值如何成为 Python 中的方法?
>>> class A(object): pass
>>> def func(cls): pass
>>> A.func = func
>>> A.func
<unbound method A.func>
这个作业如何创建一个方法?赋值对类执行以下操作似乎不直观:
- 将函数转换为未绑定的实例方法
- 将 classmethod() 中包装的函数转换为类方法(实际上,这非常直观)
- 将 staticmethod 中包装的函数() 到函数中
似乎第一个,应该有一个 instancemethod()
,而对于最后一个,根本不应该有包装函数。我知道这些是在 class
块内使用的,但为什么它们应该应用于它的外部呢?
但更重要的是,将函数分配到类中究竟是如何进行的?到底发生了什么魔法可以解决这三件事呢?
更令人困惑的是:
>>> A.func
<unbound method A.func>
>>> A.__dict__['func']
<function func at 0x...>
但我认为这与检索属性时的描述符有关。我认为这与这里的属性设置没有太大关系。
>>> class A(object): pass
>>> def func(cls): pass
>>> A.func = func
>>> A.func
<unbound method A.func>
How does this assignment create a method? It seems unintuitive that assignment does the following for classes:
- Turn functions into unbound instance methods
- Turn functions wrapped in
classmethod()
into class methods (actually, this is pretty intuitive) - Turn functions wrapped in
staticmethod()
into functions
It seems that for the first, there should be an instancemethod()
, and for the last one, there shouldn't be a wrapper function at all. I understand that these are for uses within a class
block, but why should they apply outside of it?
But more importantly, how exactly does assignment of the function into a class work? What magic happens that resolves those 3 things?
Even more confusing with this:
>>> A.func
<unbound method A.func>
>>> A.__dict__['func']
<function func at 0x...>
But I think this is something to do with descriptors, when retrieving attributes. I don't think it has much to do with the setting of attributes here.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你是对的,这与描述符协议有关。描述符是 Python 中实现将接收者对象作为方法的第一个参数传递的方式。您可以阅读有关 Python 属性查找的更多详细信息
因此,在类或实例上查找函数将其转换为方法,而不是将其分配给类属性。
classmethod
和staticmethod
只是描述符略有不同,classmethod 返回绑定到类型对象的绑定方法对象,而 staticmethod 仅返回原始函数。You're right that this has something to do with descriptor protocol. Descriptors are how passing the receiver object as the first parameter of a method is implemented in Python. You can read more detail about Python attribute lookup from here. The following shows on a bit lower level, what is happening when you do A.func = func; A.func:
So it's the looking up of the function on a class or an instance that turns it into a method, not assigning it to a class attribute.
classmethod
andstaticmethod
are just slightly different descriptors, classmethod returning a bound method object bound to a type object and staticmethod just returns the original function.描述符是神奇的1,可以将普通的当您从实例或类检索它时,将函数放入绑定或未绑定方法中,因为它们都只是需要不同绑定策略的函数。
classmethod
和staticmethod
装饰器实现其他绑定策略,而staticmethod
实际上只返回原始函数,这与从非-函数可调用
对象。请参阅“用户定义的方法”了解一些血淋淋的细节,但请注意这:
因此,如果您想要为自己的可调用对象进行此转换,您可以将其包装在函数中,但您也可以编写一个描述符来实现您自己的绑定策略。
这是正在运行的
staticmethod
装饰器,在访问它时返回底层函数。而具有
__call__
方法的普通对象不会被转换:1 具体函数是 Objects/funcobject.c。
Descriptors are the magic1 that turns an ordinary function into a bound or unbound method when you retrieve it from an instance or class, since they’re all just functions that need different binding strategies. The
classmethod
andstaticmethod
decorators implement other binding strategies, andstaticmethod
actually just returns the raw function, which is the same behavior you get from a non-functioncallable
object.See “User-defined methods” for some gory details, but note this:
So if you wanted this transformation for your own callable object, you could just wrap it in a function, but you could also write a descriptor to implement your own binding strategy.
Here’s the
staticmethod
decorator in action, returning the underlying function when it’s accessed.Whereas a normal object with a
__call__
method doesn’t get transformed:1 The specific function is
func_descr_get
in Objects/funcobject.c.你必须考虑的是,在Python中 一切都是一个对象 。通过确定这一点,可以更容易地理解正在发生的事情。如果你有一个函数
def foo(bar): print bar
,你可以执行spam = foo
并调用spam(1)
,得到当然,1
。Python 中的对象将其实例属性保存在名为 __dict__ 的字典中,并带有指向其他对象的“指针”。由于 Python 中的函数也是对象,它们可以是作为简单变量进行分配和操作,传递给其他函数等。Python 的面向对象实现利用了这一点,并将方法视为属性,即对象的 __dict__ 中的函数。
实例方法第一个参数始终是实例对象本身,一般称为
self
(但这可以称为this
或banana
)。当直接在类上调用方法时,它不会绑定到任何实例,因此您必须为其提供一个实例对象作为第一个参数(A.func(A())< /代码>)。当您调用 绑定函数 (
A().func()
),该方法的第一个参数self
是隐式的,但在幕后 Python 的作用与直接调用未绑定函数并传递参数完全相同实例对象作为第一个参数。如果理解了这一点,那么分配
A.func = func
(幕后正在执行A.__dict__["func"] = func
)这一事实会给您留下一个未绑定的方法,这并不奇怪。在您的示例中,
def func(cls): pass
中的cls
实际上将传递的是类型的实例(
。当您应用self
) >Aclassmethod
或staticmethod
装饰器 只是获取函数/方法调用过程中获得的第一个参数,并将其转换为在调用该函数之前执行其他操作。classmethod
采用第一个参数,获取实例的class
对象,并将其作为第一个参数传递给函数调用,而staticmethod
只是简单地丢弃第一个参数并在没有它的情况下调用该函数。What you have to consider is that in Python everything is an object. By establishing that it is easier to understand what is happening. If you have a function
def foo(bar): print bar
, you can dospam = foo
and callspam(1)
, getting of course,1
.Objects in Python keep their instance attributes in a dictionary called
__dict__
with a "pointer" to other objects. As functions in Python are objects as well, they can be assigned and manipulated as simple variables, passed around to other functions, etc. Python's implementation of object orientation takes advantage of this, and treats methods as attributes, as functions that are in the__dict__
of the object.Instance methods' first parameter is always the instance object itself, generally called
self
(but this could be calledthis
orbanana
). When a method is called directly on theclass
, it is unbound to any instance, so you have to give it an instance object as the first parameter (A.func(A())
). When you call a bound function (A().func()
), the first parameter of the method,self
, is implicit, but behind the curtains Python does exactly the same as calling directly on the unbound function and passing the instance object as the first parameter.If this is understood, the fact that assigning
A.func = func
(which behind the curtains is doingA.__dict__["func"] = func
) leaves you with an unbound method, is unsurprising.In your example the
cls
indef func(cls): pass
actually what will be passed on is the instance (self
) of typeA
. When you apply theclassmethod
orstaticmethod
decorators do nothing more than take the first argument obtained during the call of the function/method, and transform it into something else, before calling the function.classmethod
takes the first argument, gets theclass
object of the instance, and passes that as the first argument to the function call, whilestaticmethod
simply discards the first parameter and calls the function without it.第 1 点:您定义的函数
func
作为 First-Class 对象存在 在 Python 中。第 2 点:Python 中的类将其属性存储在
__dict__
中。那么当你在 Python 中传递一个函数作为类属性的值时会发生什么?该函数存储在类的
__dict__
中,使其成为该类的一个方法,通过调用您分配给它的属性名称来访问。Point 1: The function
func
you defined exists as a First-Class Object in Python.Point 2: Classes in Python store their attributes in their
__dict__
.So what happens when you pass a function as the value of a class attribute in Python? That function is stored in the class'
__dict__
, making it a method of that class accessed by calling the attribute name you assigned it to.关于 MTsoul 对 Gabriel Hurley 的回答的评论:
不同的是
func
有一个__call__()
方法,使其“可调用”,即您可以应用()
运算符。查看 Python 文档(在该文档中搜索__call__
页)。Relating to MTsoul's comment to Gabriel Hurley's answer:
What is different is that
func
has a__call__()
method, making it "callable", i.e. you can apply the()
operator to it. Check out the Python docs (search for__call__
on that page).