简体   繁体   中英

Inconsistent behavior in Python when using a property decorator

Here's a simple file depicting some inconsistent Python (3.6) behavior. Why is it possible that Case 1 and Case 2 run but Case 3 fails, even though Case 3 is just a merger of the first two cases?

I have provided the dis output of the first two cases.

import dis # Python bytecode disassembler

class A(object):
  def __init__(self):
    self.x # In case 2 (and 3), getting x results in a function call (because they are @properties), which fails when instantiating A because y is undefined. Case 1 evaluates the reference to a function without calling it and so it does not raise an exception.

  # CASE 1: Legal
  def x(self):
    y
    pass

  '''
  # CASE 2: Legal
  @property
  def x(self):
    pass
  '''

  '''
  # CASE 3: Illegal:
  @property
  def x(self):
    y
    pass
  '''

if __name__ == '__main__':
  a = A()
  dis.dis(A)

Case 1 bytecode:

Disassembly of __init__:
  5           0 LOAD_FAST                0 (self)
              2 LOAD_ATTR                0 (x)
              4 POP_TOP
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE

Disassembly of x:
  9           0 LOAD_GLOBAL              0 (y)
              2 POP_TOP

 10           4 LOAD_CONST               0 (None)
              6 RETURN_VALUE

Case 2 bytecode:

Disassembly of __init__:
  5           0 LOAD_FAST                0 (self)
              2 LOAD_ATTR                0 (x)
              4 POP_TOP
              6 LOAD_CONST               0 (None)
              8 RETURN_VALUE

There is no inconsistency here.

When you instantiate a = A() , __init__ is called, which calls self.x , which will execute the body of x . At that point, there is no y inscope, so you get an exception.

Thanks to @chepner's comment:

In case 1, you aren't calling anything; self.x is a function reference that isn't used. In case 3, self.x actually calls the defined getter for x, which presumably is then trying to access an undefined global name.

The behavior caused by the line self.x in case 3 is fundamentally different from case 1 because case 1 doesn't call anything -- it just evaluates a reference to a function.

On the other hand, self.x in case 3 executes the body of the x method, resulting in the undefined y error.

In order to confirm @chepner's comment, I ran ax() with case 1 and got the same error as in case 3.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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