【问题标题】:A function in a class without any decorator or `self`没有任何装饰器或“自我”的类中的函数
【发布时间】:2019-02-17 17:57:28
【问题描述】:

我有以下带有函数的类:

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 参数。这只会增加这种伟大语言的简单性。

【问题讨论】:

  • “可能是因为 self 也作为参数发送” - 对。 “会是静态函数吗?” - yep!
  • 你能发布一个链接到文档中提到这样的函数将被视为@staticmethod 吗?
  • This 帖子详细解释了静态方法。
  • 您的链接页面提到了隐式 classmethod 而不是 staticmethod 之类的函数:请参阅该页面上的 rollcall 函数。

标签: python function oop


【解决方案1】:

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

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

这里,myfn 是一个实例方法,尽管它没有被正确定义为这样使用。但是,当通过 class 访问时,__get__ 的返回值只是函数对象本身,而 function 可以像静态方法一样被调用。

通过实例访问会导致对__get__ 的不同调用。如果aA 的一个实例,那么

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

这里,__get__ 本质上试图返回 myfna 的部分应用,但因为 myfn 不接受 任何 参数,所以失败了。

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

      这样的功能与@staticmethod 提供的功能不同,但确实是某种静态方法。

      使用@staticmethod,您还可以在类的实例上调用静态方法。如果A 是一个类并且A.a 是一个static method,您将能够同时执行A.a()A().a()。如果没有这个装饰器,只有第一个示例可以工作,因为对于第二个示例,正如您正确注意到的那样,“self [will] 也 [be] 作为参数发送”:

      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
      

      【讨论】:

        【解决方案4】:

        如果您定义一个其对象实现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)
        

        您可以将 this 的实例嵌入到一个类中并对其调用各种操作:

        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 的回答,我发现它有点简洁/不透明

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-31
          • 2020-06-15
          • 2011-06-06
          • 2012-03-14
          • 1970-01-01
          • 1970-01-01
          • 2021-06-24
          • 1970-01-01
          相关资源
          最近更新 更多