Python static property decorator

Python decorator to support static properties

Classes in Python do not have native support for static properties. A meta-class can rather easily add this support as shown below. Are there any problems programmers might experience if they use this implementation?

#! /usr/bin/env python3 class StaticProperty(type): def __getattribute__(cls, name): attribute = super().__getattribute__(name) try: return attribute.__get__(cls, type(cls)) except AttributeError: return attribute def __setattr__(cls, name, value): try: super().__getattribute__(name).__set__(cls, value) except AttributeError: super().__setattr__(name, value) class Test(metaclass=StaticProperty): __static_variable = None @property def static_variable(cls): assert isinstance(cls, StaticProperty) return cls.__static_variable @static_variable.setter def static_variable(cls, value): assert isinstance(cls, StaticProperty) cls.__static_variable = value def __init__(self): self.__value = None @property def value(self): assert isinstance(self, Test) return self.__value @value.setter def value(self, value): assert isinstance(self, Test) self.__value = value def main(): print(repr(Test.static_variable)) Test.static_variable = '1st Hello, world!' print(repr(Test.static_variable)) instance = Test() print(repr(instance.value)) instance.value = '2nd Hello, world!' print(repr(instance.value)) assert Test._Test__static_variable == '1st Hello, world!' assert instance._Test__value == '2nd Hello, world!' if __name__ == '__main__': main() 

My first inclination is that the property class should be sub-classed as static_property and should be checked for in StaticProperty.__new__ to ensure it is being used properly.

\$\begingroup\$ So, to be clear, you want class properties? Properties that are bound to the class not the instance. \$\endgroup\$

\$\begingroup\$ @Peilonrayz Yes, but the example code seems to illustrate that both kinds of properties are possible. Some might find it confusing, though, if there is no distinction between a class and instance property. \$\endgroup\$

1 Answer 1

  • You’ve masked a bug, in __setattr__ a property raises an AttributeError if the setter hasn’t been defined. This causes you to overwrite the property.
  • (As you’ve said) There’s no distinction between a class property and an instance property. You can change it so there is, but it doesn’t allow the property to only be defined on the class, not the instance.
  • You can just define the static properties on the metaclass. This removes a lot of the headaches.
  • If you really want to define everything onto the class not the metaclass then you can make the metaclass hoist the wanted functions into a new metaclass. This means everything works as if you only defined two metaclasses with the properties correctly defined.
class MyMeta(type): @property def class_(self): return self._class @class_.setter def class_(self, value): self._class = value @property def class_instance(self): return self._class_instance @class_instance.setter def class_instance(self, value): self._class_instance = value class Test(metaclass=MyMeta): class_instance = MyMeta.class_instance @property def instance(self): return self._instance @instance.setter def instance(self, value): self._instance = value 
class classproperty(property): pass class classinstanceproperty(property): pass class StaticProperty(type): def __new__(self, name, bases, props): class_properties = <> to_remove = <> for key, value in props.items(): if isinstance(value, (classproperty, classinstanceproperty)): class_propertiesPython static property decorator = value if isinstance(value, classproperty): to_removePython static property decorator = value for key in to_remove: props.pop(key) HoistMeta = type('HoistMeta', (type,), class_properties) return HoistMeta(name, bases, props) class Test(metaclass=StaticProperty): @classproperty def class_(self): return self._class @class_.setter def class_(self, value): self._class = value @classinstanceproperty def class_instance(self): return self._class_instance @class_instance.setter def class_instance(self, value): self._class_instance = value @property def instance(self): return self._instance @instance.setter def instance(self, value): self._instance = value 

These both pass the following tests: (I could only get your approach to work with instance and class instance)

 test = Test() test._instance = None test.instance = True assert test._instance is True assert test.instance is True test.instance = False assert test._instance is False assert test.instance is False Test._instance = None Test.instance = True Test.instance = False assert Test._instance is None test._instance = True if Test._instance is not True: print("instance can't be used after modifying class") Test._class_instance = None Test.class_instance = True assert Test._class_instance is True Test.class_instance = False assert Test._class_instance is False test = Test() test._class_instance = None test.class_instance = True assert test._class_instance is True assert Test._class_instance is False test.class_instance = False assert test._class_instance is False Test._class = None Test.class_ = True assert Test._class is True Test.class_ = False assert Test._class is False test = Test() test._class = None test.class_ = True assert test._class is None assert Test._class is False test.class_ = False assert test._class is None 

Источник

Читайте также:  Css stylesheet font bold

Изучаем Декораторы в Python

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

Простая функция

Python Функция это блок кода, который начинается с ключевого слова def, с дальнейшим названием функции. Функция может принимать от нуля и более аргументов, ключевые аргументы или сочетание этих аргументов. Функция всегда выдает результат. Если вы не определили, что именно она должна выдавать, она выдаст None. Вот очень простой пример функции, которая выдает строку:

Все что мы сделали в этом коде, это вызвали функцию и указали значение выдачи. Давайте создадим другую функцию:

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

Давайте взглянем на полную версию данного кода:

Так и работает декоратор. Мы создали одну функцию и передали её другой второй функции. Вторая функция является функцией декоратора. Декоратор модифицирует или усиливает функцию, которая была передана и возвращает модификацию. Если вы запустите этот код, вы увидите следующий выход в stdout:

Давайте немного изменим код, чтобы превратить another_function в декоратор:

Читайте также:  Графика в javascript это

Обратите внимание на то, что декоратор начинается с символа @, за которым следует название функции, которую мы собираемся «декорировать». Для получения декоратора python, вам нужно только разместить его в строке перед определением функции. Теперь, когда мы вызываем **a_function, она будет декорирована, и мы получим следующий результат:

Давайте создадим декоратор, который будет делать что-нибудь полезное.

Есть вопросы по Python?

На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!

Telegram Чат & Канал

Вступите в наш дружный чат по Python и начните общение с единомышленниками! Станьте частью большого сообщества!

Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!

Создание логируемого декоратора

Возможно, вам потребуется логировать того, что делает ваша функция. Большую часть времени логинг будет встроен внутри вашей функции. Однако, бывают случаи, когда вам нужно сделать это на уровне функции, что бы получить представление о потоке программы или, возможно, для следования тем или иным условиям бизнеса, таким как аудит. Посмотрим на небольшой декоратор, который мы можем использовать для записи названия любой функции и того, что она делает:

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

Встроенные декораторы

Python содержит несколько встроенных декораторов. Из всех этих декораторов, самой важной троицей являются:

Также существуют декораторы в различных разделах стандартной библиотеки Python. Одним из примеров является functools.wraps. Мы сосредоточимся на трех главных декораторах, указанных выше.

@classmethod и @staticmethod

Я не пользовался ими ранее, так что сделал небольшое исследование.

  • Декоратор * может быть вызван при помощи экземпляра класса, или напрямую, через собственный класс Python в качестве первого аргумента. В соответствии с документацией Python: он может быть вызван как в классе (например, C.f()), или в экземпляре (например, C().f()). Экземпляр игнорируется, за исключением его класса. Если метод класса вызван для выведенного класса, то объект выведенного класса передается в качестве подразумеваемого первого аргумента.
  • Декоратор @classmethod, в первую очередь, используется как чередуемый конструктор или вспомогательный метод для инициализации.
  • Декоратор * — это просто функция внутри класса. Вы можете вызывать их обоих как с инициализацией класса так и без создания экземпляра класса. Обычно это применяется в тех случаях, когда у вас есть функция, которая, по вашему убеждению, имеет связь с классом. По большей части, это выбор стиля.
Читайте также:  Python пишем свой браузер

Если мы взглянем на пример кода, в котором показано, как работает декоратор, это может помочь понять основные принципы:

Этот пример демонстрирует, что вы можете вызывать обычный метод и оба метода декоратора одним и тем же путем. Обратите внимание на то, что вы можете вызывать обе функции @classmethod и @staticmethod прямо из класса или из экземпляра класса. Если вы попытаетесь вызвать обычную функцию при помощи класса (другими словами, DecoratorTest.doubler(2)), вы получите ошибку TypeError. Также стоит обратить внимание на то, что последний оператор вывода показывает, что decor.static_quad возвращает обычную функцию вместо связанного метода.

Свойства Python (@property)

Python содержит очень удобный небольшой концепт, под названием property, который выполняет несколько полезных задач. Мы рассмотрим, как делать следующее:

  • Конвертация метода класс в атрибуты только для чтения;
  • Как реализовать сеттеры и геттеры в атрибут

Один из самых простых способов использования property, это использовать его в качестве декоратора метода. Это позволит вам превратить метод класса в атрибут класса. Для меня это было очень полезно, когда мне нужно сделать какую-нибудь комбинацию значений.

Дешевые просмотры Вконтакте с гарантиями Вы найдете на сервисе https://doctorsmm.com/. Помимо приятного прайса, здесь Вы сможете получить персональные условия для работы с полученным ресурсом. Так, например, Вы сможете подобрать скорость поступления страниц таким образом, чтобы она соответствовала статистике Вашего сообщества или профиля. Соответственно, Вы получаете выгодное и действительно безопасное предложение. А еще на сайте постоянно действуют крупные оптовые скидки — торопитесь сделать заказ!

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

Источник

Оцените статью