Python ссылается на переменную класса при передаче аргументов в init - PullRequest
1 голос
/ 27 июля 2011

Чего я хочу достичь:
1. Иметь переменную класса для подсчета количества созданных объектов
2. Эта переменная не должна быть доступна для объектов / других, то есть закрытых для класса
3. Если конкретный идентификатор не указан во время инициализации, используйте эту переменную счетчика для назначения object.ID
У меня есть следующий код Python

class UserClass(object) :
    __user_id_counter = 0
    def __init__(self, UserID=__user_id_counter) :
        self.UserID = UserID
        __user_id_counter += 1

myuser = UserClass()

но я получаю UnboundLocalError: local variable '_UserClass__user_id_counter' referenced before assignment
Я новичок в Python, поэтому, пожалуйста, помогите мне здесь:)

Ответы [ 3 ]

4 голосов
/ 27 июля 2011

Для доступа к __user_id_counter необходима ссылка на объект или класс.В списке аргументов self или UserClass недоступен, следовательно:

class UserClass(object) :
    __user_id_counter = 0
    def __init__(self, UserID=None) :
        self.UserID = self.__user_id_counter if UserID is None else UserID
        UserClass.__user_id_counter += 1
1 голос
/ 27 июля 2011

Начнем с того, что я переименую UserClass в User (мы знаем, что это класс) и UserID в id (это класс User, поэтому не нужно повторять «Пользователь» бит, и стиль Python рекомендует строчные буквы), и от __user_id_counter до __id_counter (то же самое). Прочитайте PEP 8 для рекомендаций по стилю Python.

Итак, наша отправная точка такова:

class User(object):
    __id_counter = 0
    def __init__(self, id=None) :
        self.id = self.__id_counter if id is None else id
        __id_counter += 1
myuser = User()
myuser2 = User()
myuser3 = User()

Теперь двойное нижнее подчеркивание (без двойного конечного подчеркивания, как в __init__ и __metaclass__) является специальным и используется для реализации частных переменных класса - читайте о закрытых переменных в руководство по Python для получения дополнительной информации. Это вызывает то, что называется «искажение имени». В итоге получается, что __id_counter переименовано в _User__id_counter во всем классе. Это и полезно, и опасно. В этом случае я думаю, что это, вероятно, просто не нужно.

Это может привести к проблеме с подклассами.

class SpecialUser(User):
    def __init__(self):
        self.__id_counter

Теперь SpecialUser() вызовет AttributeError: 'SpecialUser' object has no attribute '_SpecialUser__id_counter'.

Итак, вопрос в том, хотите ли вы, чтобы подклассы имели одинаковый ID-счетчик или их собственные ID-счетчики?

  • Если вы хотите, чтобы у них был такой же ID-счетчик, используйте User.__id_counter. Не используйте self.__class__.__id_counter или type(self).__id_counter с += 1, так как для экземпляра SpecialUser установите SpecialUser._User__id_counter на User._User__id_counter + 1, а затем SpecialUser будет использовать с этого момента _User__id_counter, что далеко от того, что вы хотите.

  • Если вы хотите, чтобы у них было их собственные ID-счетчики, начните с использования _id_counter вместо __id_counter, поскольку искажение имени приведет вас в нежелательное для вас направление. идти.

    Существует несколько подходов:

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

    • Вставьте строку _id_counter = 0 в каждом определении класса. (Если вы этого не сделаете, у вас возникнут проблемы с отправной точкой ID - сделайте их много, например, User(), User(), SpecialUser(), User(), SpecialUser(), SpecialUser(), User(), и они будут иметь идентификаторы (соответственно) 0, 1, 2, 2, 3, 4, 3, а не 0, 1, 0, 2, 1, 2.

    Дополнительная неэффективность, которая может возникнуть в результате использования подхода искажения имен, заключается в том, что подклассы будут иметь несколько счетчиков - _User__id_counter, _SpecialUser__id_counter и т. Д.

1 голос
/ 27 июля 2011

Вы должны написать self.__user_id_counter.Это также ищет переменные класса, если переменная экземпляра не найдена.

РЕДАКТИРОВАТЬ: Судхи указал, что в списке аргументов объявление self недопустимо.Чтобы обойти эту проблему, попробуйте следующее:

class UserClass(object) :
    __user_id_counter = 0
    def __init__(self, UserID=None) :
        if UserID is None:
            self.UserID = self.__user_id_counter
            self.__class__.__user_id_counter += 1
        else:
            self.UserID = UserID

myuser = UserClass()
...