Доступ к переменным-членам класса в Python?


85
class Example(object):
    def the_example(self):
        itsProblem = "problem"

theExample = Example()
print(theExample.itsProblem)

Как мне получить доступ к переменной класса? Я пробовал добавить это определение:

def return_itsProblem(self):
    return itsProblem

Тем не менее, это тоже не удается.


1
Заголовок отредактирован, на самом деле вопрос о «статических» переменных класса: stackoverflow.com/questions/707380/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Ответы:


144

Ответ в двух словах

В вашем примере itsProblemэто локальная переменная.

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

class Example(object):
    def __init__(self):
        self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Но если вам нужна настоящая переменная класса, используйте имя класса напрямую:

class Example(object):
    itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)
print (Example.itsProblem)

Но будьте осторожны с этой переменной , так как theExample.itsProblemона автоматически устанавливается равной Example.itsProblem, но не является той же самой переменной и может быть изменена независимо.

Некоторые объяснения

В Python переменные можно создавать динамически. Таким образом, вы можете сделать следующее:

class Example(object):
    pass

Example.itsProblem = "problem"

e = Example()
e.itsSecondProblem = "problem"

print Example.itsProblem == e.itsSecondProblem 

отпечатки

Правда

Следовательно, это именно то, что вы делаете с предыдущими примерами.

Действительно, в Python мы используем selfas this, но это немного больше. selfявляется первым аргументом любого метода объекта, потому что первым аргументом всегда является ссылка на объект. Это происходит автоматически, называете вы это selfили нет.

Это означает, что вы можете:

class Example(object):
    def __init__(self):
        self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

или же:

class Example(object):
    def __init__(my_super_self):
        my_super_self.itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Это точно так же. Первым аргументом метода ЛЮБОГО объекта является текущий объект, мы вызываем его selfтолько по соглашению. И вы добавляете к этому объекту просто переменную, как если бы вы это делали извне.

Теперь о переменных класса.

Когда вы это сделаете:

class Example(object):
    itsProblem = "problem"


theExample = Example()
print(theExample.itsProblem)

Вы заметите, что мы сначала устанавливаем переменную класса , а затем получаем доступ к переменной объекта (экземпляра) . Мы никогда не устанавливаем эту объектную переменную, но она работает, как это возможно?

Ну, Python пытается сначала получить переменную объекта, но если не может ее найти, выдаст вам переменную класса. Предупреждение: переменная класса совместно используется экземплярами, а переменная объекта - нет.

В заключение, никогда не используйте переменные класса для установки значений по умолчанию для переменных объекта. Используйте __init__для этого.

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


вы говорите: «theExample.itsProblem автоматически устанавливается равным Example.itsProblem, но это совсем не та же переменная и может быть изменена независимо» - но это не совсем верно, и ваша фраза вводит в заблуждение. Я надеюсь , вы знаете , что там происходит, поэтому я предлагаю это перефразировать , что: «это есть та же переменная, но она может быть rebinded независимо для каждого объекта».
jsbueno 08

Да, но привязка - это концепция для другого языка программирования, такого как Java или C (как я подозреваю, OP), которая полностью неизвестна. Затем я хотел бы объяснить, что такое привязка, затем поздняя привязка, а затем проблема со ссылками на изменяемые объекты. Это было бы слишком долго. Я думаю, что когда-нибудь вы должны пожертвовать точностью на алтаре понимания.
e-satis

Также есть (я думаю, что это может быть 2.7+) декоратор @classmethod, на который стоит обратить внимание. интересное обсуждение здесь - stackoverflow.com/questions/12179271/…
jsh 08

1
привязка имени: я думаю, что давать ложные утверждения, независимо от уровня, - плохая идея. Я предлагаю это небольшое изменение: но будьте осторожны с этим, поскольку theExample.itsProblemон автоматически устанавливается равным Example.itsProblem, но с практической точки зрения * это совсем не та же переменная и может быть изменен независимо. *: на самом деле он начинается как один и тот же объект, но очень легко случайно изменить это, если вы не понимаете привязку
имен

11

Вы объявляете локальную переменную, а не переменную класса. Чтобы установить переменную экземпляра (атрибут), используйте

class Example(object):
    def the_example(self):
        self.itsProblem = "problem"  # <-- remember the 'self.'

theExample = Example()
theExample.the_example()
print(theExample.itsProblem)

Чтобы установить переменную класса (также известную как статический член), используйте

class Example(object):
    def the_example(self):
        Example.itsProblem = "problem"
        # or, type(self).itsProblem = "problem"
        # depending what you want to do when the class is derived.

1
За исключением того, что переменные класса объявляются один раз на уровне класса . Ваш способ будет сбрасывать его при каждой инстанции, это действительно не то, что вам нужно. Также см. Stackoverflow.com/questions/2709821/python-self-explained для обоснования явного self.

2

Если у вас есть функция экземпляра (т.е. та, которая передается self), вы можете использовать self, чтобы получить ссылку на класс, используя self.__class__

Например, в приведенном ниже коде tornado создает экземпляр для обработки запросов на получение, но мы можем получить get_handlerкласс и использовать его для хранения клиента riak, поэтому нам не нужно создавать его для каждого запроса.

import tornado.web
import riak

class get_handler(tornado.web.requestHandler):
    riak_client = None

    def post(self):
        cls = self.__class__
        if cls.riak_client is None:
            cls.riak_client = riak.RiakClient(pb_port=8087, protocol='pbc')
        # Additional code to send response to the request ...
    

Это очень хороший пример, демонстрирующий вопрос из исходного заголовка OP. На самом деле пример OP не соответствует названию, а другие ответы относятся к примеру. В любом случае, это лучший способ получить доступ к переменной уровня класса, потому что он не нарушает принцип DRY. Как и в некоторых других примерах. Лучше использовать self .__ class__ вместо повторения имени класса. Это делает код перспективным, упрощает рефакторинг, и если вы осмелитесь использовать подклассы, это также может иметь преимущества.
mit

Следует предупредить читателей, что использование обработчика, подобного описанному выше, может привести к проблемам не только в не однопоточных приложениях. Теоретически несколько экземпляров класса могут использовать один и тот же обработчик параллельно, и если обработчик не предназначен для этого, что зависит от его внутренней структуры, он может не работать. «Параллельно» в однопоточном приложении означает, что первый экземпляр класса не завершил использование обработчика, а затем второй экземпляр начинает использовать обработчик. В таких случаях можно было бы посоветовать вместо этого использовать переменную экземпляра.
mit

0

Реализуйте оператор return, как в примере ниже! У тебя должно быть хорошо. Надеюсь, это кому-то поможет ..

class Example(object):
    def the_example(self):
        itsProblem = "problem"
        return itsProblem 


theExample = Example()
print theExample.the_example()

1
Вы должны исправить отступы в коде. На этот вопрос также есть превосходные ответы, и ваш ответ представляет собой базовый вариант этих ответов, а не альтернативное решение ...
HEADLESS_0NE
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.