Подтвердить что ты не робот

Python: Что происходит, когда атрибут класса, атрибут экземпляра и метод имеют одно и то же имя?

Как python различает атрибут класса, атрибут экземпляра и метод, когда имена одинаковы?

class Exam(object):

    test = "class var"

    def __init__(self, n):
        self.test = n

    def test(self):
        print "method : ",self.test

test_o = Exam("Fine")

print dir(test_o)

print Exam.test
print test_o.test
test_o.test()

Выход:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__',    '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'test']
<unbound method load.test>
Fine
Traceback (most recent call last):
  File "example.py", line 32, in <module>
    test_o.test()
TypeError: 'str' object is not callable

Как позвонить

  • атрибут класса Exam.test<unbound method load.test> выводит метод
  • атрибут экземпляра test_o.test"Fine"
  • метод test_o.test()TypeError: 'str' object is not callable
4b9b3361

Ответ 1

Атрибуты класса доступны через класс:

YourClass.clsattribute

или через экземпляр (если экземпляр не перезаписал атрибут класса):

instance.clsattribute

Способы, как указано в ecatmur в его ответе, являются дескрипторами и устанавливаются как атрибуты класса.

Если вы получаете доступ к методу через экземпляр, то экземпляр передается в качестве параметра self в дескриптор. Если вы хотите вызвать метод из класса, то вы должны явно передать экземпляр в качестве первого аргумента. Таким образом, они эквивалентны:

instance.method()
MyClass.method(instance)

Используя одно и то же имя для атрибута экземпляра и метода, этот метод будет скрыт через экземпляр, но метод все еще доступен через класс:

#python3
>>> class C:
...     def __init__(self):
...         self.a = 1
...     def a(self):
...         print('hello')
... 
>>> C.a
<function a at 0x7f2c46ce3c88>
>>> instance = C()
>>> instance.a
1
>>> C.a(instance)
hello

Заключение: не указывайте одно и то же имя атрибутам и методам экземпляра. Я избегаю этого, давая значащие имена. Методы - это действия, поэтому я обычно использую для них глаголы или предложения. Атрибуты - это данные, поэтому я использую для них существительные/прилагательные, и это позволяет избежать использования одинаковых имен для обоих методов и атрибутов.

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

Ответ 2

Вы можете написать

Exam.test(test_o)

или

Exam.test.__get__(test_o)()

В последнем случае вы используете тот факт, что методы дескрипторы преобразуют <unbound method load.test> в связанный метод, поэтому вы можете назовите его одиночными скобками.

Когда вы пишете test_o.test(), Python не знает, что вы пытаетесь вызвать метод; вы можете попытаться вызвать функцию или вызываемый объект, который был установлен на объекте в качестве члена данных экземпляра. Вместо этого он ищет атрибут test, сначала на объекте, а затем на его классе, но поскольку атрибут существует на объекте, он скрывает метод в классе.

Член класса

test = "class var"

недоступен (на самом деле он нигде не существует), поскольку он перезаписывается методом test; когда выполняется class statement, его пространство имен собирается в dict перед передачей его метаклассу, а затем имена переопределяют более ранние.

Ответ 3

Вы можете вызвать метод как метод класса и передать свой экземпляр в него:

Exam.test(test_o)

Или, если вы не хотите использовать Exam:

type(test_o).test(test_o)

Ответ 4

Как позвонить | class, Exam.test

Вы не можете, потому что при выполнении def test(self) имя test привязано к методу в классе и ссылка на "class var" теряется.

атрибут экземпляра test_o.test → "Fine"

Вы уже это сделали.

метод test_o.test()

Вы не можете называть это так, потому что при выполнении self.test = n имя test привязано к любому объекту n ссылок в экземпляре и ссылка на метод в экземпляре теряется.

Но как указано в других ответах, вы можете вызвать метод в классе и передать ему экземпляр: Exam.test(test_o)