没有任何修饰符或`self`

时间:2019-02-17 17:57:28

标签: python function oop

我有一个带有以下功能的课:

class A: 
    def myfn():
        print("In myfn method.")

此处,该函数没有self作为参数。它还没有@classmethod@staticmethod作为装饰器。但是,如果与以下类一起调用,它将起作用:

A.myfn()

输出:

In myfn method.

但是如果从任何实例调用,都会出错:

a = A()
a.myfn()

错误输出:

Traceback (most recent call last):
  File "testing.py", line 16, in <module>
    a.myfn()
TypeError: myfn() takes 0 positional arguments but 1 was given

可能是因为self也是作为参数发送的。

将调用哪种功能?它会是一个静态函数吗?在类中使用这样的函数是否明智?缺点是什么?

编辑:仅当使用类而不是对象/实例调用时,此函数才起作用。我的主要问题是这样的函数叫什么?

Edit2:从答案中可以看出,尽管这种功能是最简单的形式,却不被视为合法。但是,由于在许多答案中都没有提到严重的缺点,因此我发现这是一个有用的构造,尤其是将我自己的静态函数分组到一个我可以根据需要调用的类中时。我不需要创建此类的任何实例。至少,它使我不必每次都键入@staticmethod,并使代码看起来不太复杂。对于某人扩展我的课程,它也很巧妙。尽管所有这些功能都可以保留在顶级/全局级别,但将它们保留在类中则更加模块化。但是,我觉得应该以这种特定方式起作用的这种简单构造应有一个特定的名称,并且应该被认为是合法的。它还可以帮助初学者了解Python类中的常规函数​​为何需要self参数。这只会增加这种出色语言的简单性。

4 个答案:

答案 0 :(得分:3)

function类型实现了描述符协议,这意味着当您通过该类或该类的实例访问myfn时,您不会获得实际的函数。您将获得该函数的__get__方法的结果。也就是说,

A.myfn == A.myfn.__get__(None, A)

在这里,myfn是一种实例方法,尽管尚未正确定义该实例方法。但是,通过 class 访问时,__get__的返回值只是函数对象本身,而 function 可以称为静态方法。

通过实例访问将导致对__get__的不同调用。如果aA的实例,则

a.myfn() == A.myfn.__get__(a, A)

在这里,__get__试图将myfn的部分应用返回到a,但是因为myfn不需要任何参数,失败。

您可能会问,什么是静态方法? staticmethod是一种包装函数并定义自己的__get__方法的类型。无论是否通过类或实例访问属性,该方法都将返回基础函数。否则,静态方法和普通函数之间几乎没有什么区别。

答案 1 :(得分:2)

这不是一个正确的方法。正确声明的实例方法应该有一个self参数(名称只是一个约定,如果您想阅读难以理解的代码可以更改),并且类方法和静态方法应由它们各自的修饰符引入。

但是在较低级别,类声明中的def只是创建一个函数并将其分配给类成员。这就是这里发生的情况:A.my_fn是一个函数,可以成功地称为A.my_fn()

但是由于它不是用@staticmethod声明的,所以它不是真正的静态方法,因此不能应用于A实例。 Python看到该名称的成员恰好是既不是静态方法也不是类方法的函数,因此它将当前实例添加到参数列表中并尝试执行。

要回答您的确切问题,这不是方法,而只是恰好分配给类成员的函数。

答案 2 :(得分:1)

这种功能与@staticmethod提供的功能不同,但实际上是一种静态的排序方法。

使用@staticmethod,您还可以在类的实例上调用静态方法。如果A是类,而A.astatic method,则可以同时进行A.a()A().a()。如果没有此修饰符,则只有第一个示例有效,因为对于第二个示例,正如您正确注意到的那样,“ {self [也]也将作为参数发送”:

class A:
    @staticmethod
    def a():
        return 1

运行此:

>>> A.a()  # `A` is the class itself
1
>>> A().a()  # `A()` is an instance of the class `A`
1

另一方面:

class B:
    def b():
        return 2

现在,第二个版本不起作用:

>>> B.b()
2
>>> B().b()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: b() takes 0 positional arguments but 1 was given

答案 3 :(得分:1)

如果您定义了一个类,其对象实现了descriptor protocol,则类似于@chepnet的答案:

class Descr:
    def __get__(self, obj, type=None):
        print('get', obj, type)
    def __set__(self, obj, value):
        print('set', obj, value)
    def __delete__(self, obj):
        print('delete', obj)

您可以将其实例嵌入到类中并在其上调用各种操作:

class Foo:
    foo = Descr()

Foo.foo
obj = Foo()
obj.foo

输出:

get None <class '__main__.Foo'>
get <__main__.Foo object at 0x106d4f9b0> <class '__main__.Foo'>

由于函数还实现了描述符协议,因此我们可以通过以下方式重播该协议:

def bar():
    pass

print(bar)
print(bar.__get__(None, Foo))
print(bar.__get__(obj, Foo))

输出:

<function bar at 0x1062da730>
<function bar at 0x1062da730>
<bound method bar of <__main__.Foo object at 0x106d4f9b0>>

希望能补充chepnet的答案,我发现它有点简洁/不透明