Upload
pynsk
View
592
Download
7
Embed Size (px)
Citation preview
Дескрипторы. Что это?
Донец Владимир
twitter.com/vovk_donetsvk.com/vovk.donets
Магия в Python
e-commerce SAAS kwimba.ru
2
О чем пойдет речь
➢ Что такое дескриптор➢ Как его можно применять в своём коде➢ Как устроены дескрипторы➢ Примеры кода из жизни
3
Модифицировать поведение атрибута price класса Product таким образом, чтобы сохранить существующий API.
Задача
Есть идеи?
4
class Product(object):
…
>>> p = Product(...)
>>> p.price
1 000
Немного псевдокода
Есть идеи?
5
Переопределить
__getattr__
__setattr__
__getattribute__
Например, можно...
Это решение или новая проблема?
6
» Вызываются при доступе к любому аттрибуту*
» Логика доступа и присванивания сильно усложняется
» *К тому же __getattr__ вызывается только в случае, если атрибут не был найден обычным способом
» Снижает скорость присвоения всех атрибутов
Минусы __get/__setattr__
7
class PriceFieldDescriptor(object):
def __init__(self, name):
self.name = name
def __get__(self, instance=None, owner=None):
return instance.__dict__[self.name]
def __set__(self, instance, value):
instance.__dict__[self.name] = value
Что это за класс?
8
Внимание, дескриптор!
Просто класс, в котором определены методы: __get__, __set__, __delete__
descr.__get__(self, inst, type) ─> value
descr.__set__(self, inst, value) ─> None
descr.__delete__(self, inst) ─> None
properties, methods, static methods, class methods = дескрипторы
9
class PriceField(...):
...
price = PriceFieldDescriptor(...)
...
>>> p = Product(...)
>>> p.price ─> вызовет __get__ дескриптора
>>> p.price = 100 ─> вызовет __set__ дескриптора
>>> del p.price ─> вызовет __del__ дескриптора
Решение
10
Почему это работает?
11
__getattribute__
А вот благодаря чему:
Доступ к атрибуту экземпляра b.x превратится:
type(b).__dict__['x'].__get__(b, type(b))
Доступ к атрибуту класса B.x:
B.__dict__['x'].__get__(None, B)
12
Вызов дескриптора:
Прямой вызов дескриптора:x.__get__(a)
Вызов дескриптора из экземпляра:type(a).__dict__['x'].__get__(a, type(a))
Вызов дескриптора из класса: A.__dict__['x'].__get__(None, A)
И super: super(B, obj).m() searches obj.__class__.__mro__A.__dict__['x'].__get__(obj, obj.__class__).
13
class PriceField(PositiveDecimalField):
def __init__(self, *args, **kwargs):
super(PriceField, self).__init__(*args, **kwargs)
def contribute_to_class(self, cls, name):
super(PriceField, self).contribute_to_class(cls, name)
setattr(cls, name, PriceFieldDescriptor(name))
Решение
14
def readonly(value):
return property(lambda self: value)
class ReadOnlyClass(object):
a = readonly(1)
b = readonly('text')
Пример из жизни №2
15
class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError, "unreadable attribute"
return self.fget(obj)
Пример из жизни №3
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError, "can't set attribute"
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError, "can't delete
attribute"
self.fdel(obj)
16
Ресурсы
● http://users.rcn.com/python/download/Descriptor.htm#invoking-descriptors
● https://www.python.org/download/releases/2.2.3/descrintro/#cooperation
● http://martyalchin.com/2007/nov/23/python-descriptors-part-1-of-2/
● http://blog.kevinastone.com/django-model-descriptors.html
● http://pyvideo.org/video/3123/descriptors-attribute-access-redefined-by-fraser
● http://www.catharinegeek.com/the-magic-of-django-model/
● http://habrahabr.ru/post/137415/