питон - странный вопрос поведения - PullRequest
1 голос
/ 06 сентября 2010
>>> 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

Почему в этом примере s.x является методом, но также целым числом?Мне кажется, что self.x = 1 должен заменить объявление def x(self): атрибута x во время создания экземпляра.Почему я могу получить и вызвать, в результате чего у целого и метода, соответственно, один и тот же атрибут?Я предполагаю, что шаблон поиска переменных в классах нового стиля напечатан по типу утки, чтобы возвращать вызывающему наиболее подходящий результат.Я хотел бы услышать всю историю.

Ответы [ 5 ]

3 голосов
/ 06 сентября 2010

Python не использует отдельные пробелы для вызываемых и не вызываемых объектов: имя - это имя - это имя. s.x, по правилам Python, должен ссылаться на точно того же объекта, независимо от того, собираетесь ли вы вызывать его или нет. Другой способ выразить это: предполагая, что _aux - это имя, которое не используется иным образом,

_aux = self.x
_aux()

и

self.x()

должен иметь абсолютно идентичную семантику в Python, тот факт, что в первом промежуточное значение self.x привязывается к имени и вызывается позже, несмотря на это.

Наличие единых, «унифицированных» пространств имен для вызываемых и не вызываемых абонентов имеет огромное количество преимуществ - это делает правила поиска имен (для каждого из голых и квалифицированных имен) чрезвычайно простыми, например, полностью отделяя их от цель , в которую будет помещено искомое имя (будь то сразу после результата поиска или еще позже), а также из типа (вызываемого или не вызываемого) вызываемый) любого объекта, на который оказывается первая ссылка в соответствии с правилами поиска.

Особенно с учетом того, сколько различных вызываемых типов есть в Python (функции, классы, экземпляры классов, которые определяют __call__, специальные типы, такие как staticmethod и classmethod, ...! -), любое другое правило может только привести к полному хаосу. (Также обратите внимание, например, что даже C ++, язык, который определенно не боится сложности, но который также позволяет экземплярам классов вызываться [[если класс перегружает operator()]], использует аналогичный правило унифицированного пространства имен - опять же, различие между вызываемыми и не вызываемыми объектами было бы совершенно необоснованным кошмаром, если бы правила были иными в этом отношении! -).

2 голосов
/ 06 сентября 2010

Похоже, вы неправильно понимаете ошибку, которую видите. Когда создается объект s, его конструктор заменяет метод x на целое число, поэтому в объекте s x является целым числом, а не функцией. Попытка вызвать его как метод приводит к исключению.

Python типизирован уткой в ​​том смысле, что вызовы методов разрешаются во время выполнения - у компилятора нет проблем с s.x(), потому что x мог быть создан как метод динамически. Однако, когда интерпретатор фактически вызывает x как метод, он замечает, что x является целым числом и не может быть вызван, следовательно, TypeError.

1 голос
/ 06 сентября 2010

Я не уверен, что вы думаете, что происходит, но ничего сложного не происходит. Когда вы назначаете self.x = 1, метод x больше не доступен. С этого момента, s.x является только целым числом - попытки вызвать его как метод приводят к исключению, как вы видели.

0 голосов
/ 06 сентября 2010

Вот что делает ваш код:

  1. Создайте класс с именем S с 2 методами, __init__ и x
  2. Создайте экземпляр S и назовите его s
    1. Вызов S.__init__ с s в качестве параметра
      1. Установить s.x со значением 1
  3. Печать s.x
  4. Распечатать результат вызова s.x

Теперь, если вы посмотрите в 2.1.1, вы увидите, что вы заменили метод x на целое число, что означает, что вы не можете вызвать его снова с s (но он все еще остается в классе S)

Если вы это сделали, и все же вам нужно вызвать функцию x, попробуйте:

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

Я только что сделал это, чтобы вы понимали, почему вы теряете метод x as, делаете это правильно и избегаете иметь переменные экземпляров с тем же именем, что и методы класса

0 голосов
/ 06 сентября 2010

Кажется, что свойство x определено как метод в определении класса.Однако на самом деле создание экземпляра объекта перезаписывает это имя целым числом - следовательно, наблюдается поведение.Это на самом деле никогда не два сразу.Итак, это в основном какой-то неисправный код.

...