Могу ли я создать неизменяемый атрибут класса в Python 3?

Я пишу класс с пользовательскими методами __eq__ и __hash__, и __hash__ по сути возвращает один из атрибутов класса (который является целым числом). Поэтому мне нужно сделать этот атрибут максимально неизменяемым. Есть ли способ сделать атрибут неизменяемым в Python?

Могу, конечно, успеть __private, но это не совсем то. Кроме того, на данный момент я не уверен, следует ли мне сделать мой класс классом данных.

Дополнительный контекст, если это как-то актуально: класс Simplex представляет комбинаторный симплекс и хранит набор вершин (в форме двоичного числа) и целочисленный коэффициент. В классе есть метод boundary(), который должен возвращать объект типа Complex, представляющий собой коллекцию экземпляров Simplex. Метод __eq__ думает A == B, совпадают ли их множества вершин (коэффициенты не имеют значения). Метод __hash__ возвращает двоичные вершины хранения. Я хочу, чтобы __hash__ работал именно таким образом, потому что думаю, что это упростит сложение равных симплексов в Complex.

Простой ответ: «нет». Рекомендуемый метод скрыть что-либо — __private. Но в Python на самом деле нет такого понятия, как переменная private, поэтому человек, запускающий программу, всегда может копать где угодно.

Frank Yellin 14.04.2024 18:15
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Попробуйте использовать собственный декоратор class_property.

Что-то вроде (копия из этого поста https://stackoverflow.com/a/5191224/24285005):


class ClassPropertyDescriptor(object):

    def __init__(self, fget, fset=None):
        self.fget = fget
        self.fset = fset

    def __get__(self, obj, klass=None):
        if klass is None:
            klass = type(obj)
        return self.fget.__get__(obj, klass)()

def classproperty(func):
    if not isinstance(func, (classmethod, staticmethod)):
        func = classmethod(func)

    return ClassPropertyDescriptor(func)


class Bar(object):
    @classproperty
    def bar(cls):
        return 1

Фрэнк Йеллин совершенно прав, говоря, что в Python нет настоящих частных переменных.

Однако в Python есть возможность изменить способ доступа к атрибутам и их редактирования. Это можно использовать для изменения способа взаимодействия класса с окружающим кодом, например, добавление проверки — одна из основных причин, по которой вы можете захотеть, чтобы атрибут был закрытым.

Ниже приведен пример, как реализовать это с помощью геттера @property и сеттера @brand.setter декоратора.

class GermanCar:
    def __init__(self, model, brand):
        self.model = model
        self.brand = brand

    def __str__(self):
        return f"The car brand is {self.brand} and the model is {self.model}"
    
    @property
    def brand(self):
        return self._brand
    
    @brand.setter
    def brand(self, brand):
        print(brand)
        if brand not in {"BMW", "Mercedes", "Volkswagen"}:
            raise ValueError(f"This class only allows German car brands, {brand} isn't.")
        self._brand = brand


def build_car():
    brand = input("Brand: ")
    model = input("Model: ")
    return GermanCar(model, brand)

car = build_car()
print(car)

Преимущество вышеизложенного заключается в том, что атрибут, с которым пользователи будут взаимодействовать car.brand, отделен от атрибута, который используется для хранения значения car._brand, тем самым снижая риски случайных изменений.

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

Однако это не делает ее действительно неизменяемой переменной, а просто еще одним уровнем безопасности. Если кто-то действительно намеревается взломать ваш код и хочет обойти проверку, он может просто отредактировать car._brand напрямую, минуя проверку.

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

Другие вопросы по теме