Python - принудительная ошибка, если в классе используется необъявленная переменная - PullRequest
3 голосов
/ 29 сентября 2019

Рассмотрим следующий код Python:

class a_class:
    defined_var = 1

    def a_function(self):
        self.defined_var = 2
        self.undefined_var = 3 

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

Я знаю, что этот вопрос может показаться глупым. Я давно разрабатывал на C / C ++ / Java, где определение переменных в классе является обязательным ...

Есть ли способ избежать этого? Я имею в виду, я хотел бы поведение, как в C / C ++ / Java, где я получаю ошибку, если я использую неопределенную переменную.

Ответы [ 3 ]

5 голосов
/ 29 сентября 2019

По умолчанию экземпляры python имеют __dict__ - словарь, в котором хранятся все атрибуты объекта. Это то, что позволяет им хранить произвольные атрибуты. Вы можете подавить создание __dict__, определив __slots__. Это переменная уровня класса, в которой перечислены имена всех атрибутов, которые могут иметь экземпляры класса. Если класс имеет __slots__, попытка присвоить неопределенному атрибуту вызовет исключение.

Пример:

class MyClass:
    __slots__ = ['defined_var']

    def a_function(self):
        self.defined_var = 2

obj = MyClass()
obj.a_function()
obj.undefined_var = 3  # throws AttributeError
3 голосов
/ 29 сентября 2019

Вы можете переопределить object.__setattr__:

class a_class:
    defined_var = 1

    def a_function(self):
        self.defined_var = 2
        self.undefined_var = 3

    def __setattr__(self, key, value):
        if hasattr(self, key):
            object.__setattr__(self, key, value)
        else:
            raise AttributeError(f"\"{key}\" is not defined in \"{self.__class__.__name__}\"")

a = a_class()
print(a.defined_var)
a.a_function()  # <-- throws an error

PS defined_var - переменная класса, общая для всех экземпляров. Это означает, что все изменения будут влиять на все экземпляры класса (возможно, это не то, что вы ожидаете). Больше информации здесь .

1 голос
/ 30 сентября 2019

Просто добавьте сюда несколько заметок:

Создатель Python, Гвидо ван Россум, утверждает, что он фактически создал слотов для более быстрого доступа к атрибутам.

из книги Свободный питон , __slots__ используется для более быстрого доступа к атрибутам и сокращения использования памяти, чтобы не мешать пользователю добавлять дополнительные атрибуты.

проверьте этот код:

class MyClass:
    __slots__ = ['defined_var', '__dict__']

obj = MyClass()
obj.undefined_var = 3  # works fine

Проблема в использовании __slots__. Если вы создаете подкласс этого класса, вам нужно повторить в нем атрибут __slot__.

class MyClass:
    __slots__ = ['defined_var']

class MySubClass(MyClass):
    # you need to redefine the __slots__ here too
    pass

obj = MyClass()
sub_obj = MySubClass()
sub_obj.undefined_var = 3  # works fine not excepting this
obj.undefined_var = 3  # throw exception

Итак, если вам действительно это нужно, перейдите к переопределению метода setattr, как предложил @Olvin Roght.

class MyClass:
    x = None 
    def __setattr__(self, key, value):
        if hasattr(self, key):
            object.__setattr__(self, key, value)
        else:
            raise AttributeError(f"\"{key}\" is not defined in \"{self.__class__.__name__}\"")


class MySubClass(MyClass):
    pass


obj = MyClass()
sub_obj = MySubClass()
sub_obj.x = 3  # works fine
obj.x = 3  # works fine
sub_obj.undefined_var = 3  # error
obj.undefined_var = 3  # error  

Если когда-нибудь вы захотите сделать исправление обезьян на вашем объекте:

obj = MyClass()
obj.__dict__['z'] = 4
print(obj.z)  # print 4, we added a new attribute

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

...