【问题标题】:Inner class function without self没有自我的内部类函数
【发布时间】:2018-04-13 17:30:03
【问题描述】:

大家和平! 我正在使用 Python 3.6.3,但奇怪的是这样的构造是可能的:

class TestClass(object):
    def __init__(self):
        self.arg = "arg"

    def test():
        print("Hey test")

并使用:

>>> TestClass.test()
"Hey test"

我知道Python中有以self为参数的标准方法(不知道如何正确调用)、静态方法、类方法、抽象方法。

但是test()是什么方法呢? 是静态方法吗?


已编辑:

这种确定类内函数的方法有什么有用的用例吗?

【问题讨论】:

  • 为什么你觉得这段代码很奇怪?
  • test 是一个不带参数的函数,您在不带参数的情况下调用它。都好。现在,TestClass().test()OTOH 是另一回事了……
  • 是的。把“嘿”改成小写很奇怪。
  • self.test 的模棱两可是问题的一部分,还是只是巧合?
  • @tobias_k 对不起,这只是巧合,我已经解决了

标签: python python-3.x class


【解决方案1】:

让我用一个例子来解释:

class TestClass(object):
  def __init__(self):
    self.arg = "arg"

  def test1():
    print("class method test1, Hey test")

  @classmethod
  def test2(cls):
    print("class method test2, Hey test")

  def test3(self):
    print("instance method test3, Hey test")

看看当你用类或实例调用 test1 时会发生什么:

第一:

  TestClass.test1() #called from class
class method test1, Hey test
   TestClass().test1() #created an instance TestClass()
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: test1() takes 0 positional arguments but 1 was given

那是因为在创建实例的时候,使用了self参数,而这里的方法没有self参数,所以才会刹车。

下一个!

   TestClass.test2()
class method test2, Hey test
   TestClass().test2()
class method test2, Hey test

这对例如和类都有效,为什么?好吧,你可以看到 test2(cls) 有一个参数,cls,在这里,我没有使用它,所以,它可以工作。

给我带来下一个主题,muajaja

  TestClass().test3()
instance method test3, Hey test
   TestClass.test3()
Traceback (most recent call last):
  File "python", line 1, in <module>
TypeError: test3() missing 1 required positional argument: 'self'

这很容易看出,当你把它作为类调用时,你没有提供self参数

【讨论】:

    【解决方案2】:

    TestClass.test() 调用实际上在 class 对象上执行 test()。这类似于@staticmethod(可以在类对象上执行的方法,无需先创建对象)。

    ins = TestClass()
    ins.test()
    

    将抛出异常,因为 实例方法self 作为第一个参数传递,而 test() 不接受任何参数。

    创建对象时,类中定义的方法绑定到它。它们实际上是不同的对象,因此它们具有不同的 id:

       id(TestClass.test)
    => 140288592901800
       obj = TestClass()
       id(obj.test)
    => 140288605765960
    

    在 Python 2.7 中,您的代码会引发一个不言自明的异常:

    Traceback (most recent call last):
      File "<pyshell#404>", line 1, in <module>
        TestClass.test()
    TypeError: unbound method test() must be called with TestClass instance as 
    first argument (got nothing instead)
    

    【讨论】:

    • @staticmethod 可以从类的实例中调用,但在我的示例中这是不可能的
    • 这是因为在实例方法上调用任何东西时,都会自动传递self部分。如果你的函数不带任何参数,你根本无法在实例对象上执行它。
    【解决方案3】:

    在 Python 3 中(与 Python 2 不同)从类访问和调用的函数只是另一个函数;没什么特别的:

    注意从函数对象到实例方法的转换 每次从实例中检索属性时都会发生对象。

    [强调我的]

    您只是碰巧使用正确的参数集调用函数,尽管是通过类对象访问的。与通过实例调用方法的底层函数对象相同:

    TestClass().test.__func__() # "Hey test"
    

    快速测试进一步解释:

    print(TestClass().test is TestClass.test)
    # False
    print(TestClass().test.__func__ is TestClass.test)
    # True
    

    但是,在 Python 2 中,行为是不同的,因为从函数对象到方法对象的转换发生在通过类或实例访问属性时:

    注意 transformation 从函数对象到(未绑定或 bound) 方法对象在每次检索属性时发生 类或实例。

    [强调我的]

    【讨论】:

      【解决方案4】:

      在python 3中,函数和类中定义的函数没有区别:

      def test():
          print("Hey test")
      
      class TestClass:
          def test():
              print("Hey test")
      
      test() # works
      TestClass.test() # also works
      

      这两个都是正常的功能。

      当您通过类的实例访问函数时,隐式 self 参数的魔力就会发生,如下所示:

      obj = TestClass()
      obj.test() # throws an error because the test function doesn't accept arguments
      

      这是函数 test 变成(绑定)方法 test 的时候。如果您打印它们,您可以看到差异:

      print(TestClass.test) 
      print(instance.test)
      # output:
      # <function TestClass.test at 0xaaaaaa>
      # <bound method TestClass.test of <__main__.TestClass object at 0xbbbbbb>>
      

      总结一下:

      • 通过类访问函数会得到原始函数。
      • 通过实例访问函数会为您提供一个带有绑定 self 参数的方法。

      要详细了解从函数到绑定方法的转换是如何工作的,请参阅the descriptor how-to,特别是the section about functions

      【讨论】:

        【解决方案5】:

        Python 3 取消了 Python 2 中存在的绑定和未绑定方法之间的区别。以前的未绑定方法现在只是一个常规函数。

        class A(object):
            def test():
                pass
        

        在 Python 2 中:

        >>> A.test
        <unbound method A.test>
        

        而在 Python 3 中:

        >>> A.test
        <function A.test at 0x101cbbae8>
        

        (地址可能不同)

        test 这里是一个描述符;当您访问A.test 时,您不会(必然)取回原始函数对象;相反,您将获得该对象(即函数)__get__ 方法的返回值,该方法使用两个参数调用。哪些参数取决于您是通过类还是通过类的实例访问它。

        • A.test => A.test.__get__(None, A)
        • a = A(); a.test => A.test.__get__(a, A)

        在 Python 2 中,A.test.__get__(None, A) 返回一个 method 对象,它是原始函数的包装器。作为一个未绑定method,包装器仍然期望A的实例作为它的第一个参数,即使函数本身没有使用任何参数定义。

        然而,在 Python 3 中,A.test.__get(None, A) 只是返回对原始函数的引用,而不是 method 对象。因此,您可以完全按照您最初定义的方式使用它。

        您可以通过在 Python 2 和 Python 3 中检查 id(A.__dict__['test'])id(A.test) 来确认这一点。在 Python 2 中,您将获得两个不同的值;在 Python 3 中,您将获得相同的值。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-10-17
          • 1970-01-01
          • 1970-01-01
          • 2018-10-21
          • 2015-03-19
          • 1970-01-01
          • 1970-01-01
          • 2014-04-22
          相关资源
          最近更新 更多