简体   繁体   中英

python - strange behavior question

>>> class S(object):
...     def __init__(self):
...             self.x = 1
...     def x(self):
...             return self.x
...
>>> s = S()
>>> s.x
1
>>> s.x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable

Why, in this example, is sx a method, but also an integer? It seems to me that self.x = 1 should replace the def x(self): declaration of the attribute x during instantiation. Why is it that I can get and call, resulting in an integer and a method, respectively, the same attribute? My guess is that the variable look-up pattern in new-style classes is duck typed, so as to return the most relevant result to the caller. I would love to hear the whole story.

Python doesn't use separate spaces for callable and non-callable objects: a name is a name is a name. sx , by Python rules, must refer to exactly the same object, whether you're going to call it or not. Another way of putting it: assuming that _aux is a name not otherwise used,

_aux = self.x
_aux()

and

self.x()

must have absolutely identical semantics in Python, the fact that in the former the intermediate value self.x is being bound to a name and called later notwithstanding.

Having single, "unified" namespaces for callables and non-callables has a huge number of advantages -- it makes name-lookup rules (for each of bare and qualified names) enormously simpler, for example, by decoupling them totally from the purpose to which the name being looked up is going to be put (be it immediately after the lookup's result, or later still), and also from the type (callable or non-callable) of whatever object turns up to be first referenced according to the lookup rules.

Especially considering how many different callable types Python has (functions, classes, instances of classes which define __call__ , special types such as staticmethod and classmethod , ...!-), any other rule could only lead to total chaos. (Note also, for example, that even C++, a language which definitely is not afraid by complexity but which also lets class-instances be callable [[if the class overloads operator() ]], uses a similar unified-namespace rule -- again, discriminating between callables and non-callables would be a totally unwarranted nightmare, were the rules any different in this regard!-).

It looks like you're having a misunderstanding of the error you're seeing. When your s object is instantiated, its constructor replaces the method x by an integer, so in the s object, x is an integer, not a function. Trying to call it as a method results in an exception being thrown.

Python is duck-typed in the sense that method calls are resolved at runtime - the compiler has no problem with sx() because x might have been created as a method dynamically. However, when the interpreter actually calls x as a method, it notices x is an integer and can't be called, hence the TypeError .

I'm not sure what you think is going on, but there's nothing that tricky happening. When you assign self.x = 1 , the method x is no longer accessible. From that point forward, sx is only an integer -- attempts to call it as a method result in an exception, as you saw.

It seems that the x property is defined as a method in the class definition. However, actually instantiating an object overwrites that name with an integer - hence, the behavior observed. It's never actually two at once. So, this is basically some faulty code.

This is what your code is doing:

  1. Create a class named S with 2 methods, __init__ and x
  2. Create an instance of S and name it s
    1. Call S.__init__ with s as parameter
      1. Set sx with the value 1
  3. Print sx
  4. Print the result of calling sx

Now, if you look in 2.1.1 you will see that you have overrided the method x with an integer, which means that you cannot call that again withing s (but it stills in S class)

If you have done that, and yet, need call x function, try it:

>>> class S(object):
...     def __init__(self):
...         self.x = 1
...     def x(self):
...         return self.x
... 
>>> s = S()
>>> s.x
1
>>> S.x(s)
1
>>> 

I just did it so you understand why you are losing the x as method, do it in the right way and avoid to have instances variables with the same name as class methods

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