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
Ответы
Ответ 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)