«Вингардиум левиоса»
Основы декларативной магии
Сергей Матвеенко
«Harry Potter and the Philosopher's Stone»
http://xkcd.com/353/
➢ Описания: существительные и
прилагательные, а не глаголы
➢ Независимость от реализации
➢ Валидация без компиляции
➢ Не нужно уметь программировать
➢ GUI для редактирования
➢ Потому что это модно :)
Декларативное программирование
➢ Декораторы
➢ Метаклассы
○ Атрибуты классов
○ Аргументы классов
○ Аннотации аргументов методов
➢ Import hooks
○ Модификация AST
○ Генерация кода
➢ Внешние описания
○ YAML
Декларативность в Python
Декораторы
@this_is_decorator(safe_mode=True)
def method(arg1, arg2):
# we will have just a few lines here
return arg1 + arg2
@abstractclass
class ObjectBase:
def foo(self):
return NotImplemented
Метаклассы: атрибуты классов
class MyClass(ObjectBase):
sequence = True
sorted = False
seq = MyClass()
seq.extend([3, 2, 5])
print(seq)
> [3, 2, 5]
Метаклассы: атрибуты классов
class ObjectMeta(type):
def __new__(cls, name, bases, attrs):
type_new = type.__new__(cls, name, bases, attrs)
if attrs.get('sequence', False):
# add sequence realization
if attrs.get('sorted', False):
# add sorted realization
return type_new
class ObjectBase(metaclass=ObjectMeta):
pass
class MyClass(ObjectBase):
sequence = True
sorted = False
Метаклассы: аргументы классов
class SequenceMeta(type):
def __new__(cls, name, bases, attrs, sorted=False):
type_new = type.__new__(cls, name, bases, attrs)
if sorted:
# add sorted realization
return type_new
class SortedSequence(metaclass=SequenceMeta, sorted=True):
pass
seq = SortedSequence()
Метаклассы: аннотации
import inspect
class StrictMeta(type):
def __new__(cls, name, bases, args):
type_new = type.__new__(cls, name, bases, args)
for attr_name in dir(type_new):
method = getattr(type_new.attr_name)
if callable(method):
parameters = inspect.signature(method).parameters.values()
# construct decorated method
setattr(new_type, attr_name, method)
return new_type
class TextNumber(metaclass=StrictMeta):
value = "0"
def __add__(self, value: r'[d.]+'):
# add implementation
return self.value
Import hooks: модификация AST
# smart_sql.py
class MyImporter:
def load_module(self, name):
# modify AST
return module
sys.path_hooks.insert(0, MyImporter)
# prog.py
import smart_sql
query = (
id, Point(x, y)
for id, x, y
in "sql_table_name"
if len([(x0, y0), (x, y)]) < 3)
Import hooks: генерация кода
# smart_sql.py
class MyImporter:
def find_module(self, fullname, path=None):
# find path to DSL source file
self.path = path
return self
def load_module(self, name):
# generate and compile python module from DSL
return module
sys.meta_path.insert(0, MyImporter)
# prog.py
import smart_sql
from dsl_queries import query
result = query.find(radius)
YAML
# pytest-yamlwsgi
test_index:
- path: /
assert_status: 200
assert_contains: Hello
- path: /
assert_contains: Hello
- path: /
assert_status: 200
Django ORM
from django.db import models
class Musician(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
instrument = models.CharField(max_length=100)
class Album(models.Model):
artist = models.ForeignKey(Musician)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
Django class-based generic views
class PublisherDetail(DetailView):
context_object_name = 'publisher'
queryset = Publisher.objects.all()
class BookList(ListView):
queryset = Book.objects.order_by('-publication_date')
context_object_name = 'book_list'
class AcmeBookList(ListView):
context_object_name = 'book_list'
queryset = Book.objects.filter(publisher__name='Acme')
template_name = 'books/acme_list.html'
Function annotations
# http://code.activestate.com/recipes/578528/
@typecheck
def happy1(a:int, b:list, c:tuple=(1,2,3)) -> float:
return 3.14
@typecheck
def happy_wo_annotation(a:int, b, c:tuple=(1,2,3)) ->
float:
return 3.14
@typecheck
def unhappy1(a:int, b:str) -> float:
return 314 # This can never succeed in return type
Вопросы?
github.com/lig
ptsecurity.com

"Вингардиум Левиоса”. Или основы декларативной магии (Матвеенко Сергей)

  • 1.
  • 2.
    «Harry Potter andthe Philosopher's Stone»
  • 3.
  • 4.
    ➢ Описания: существительныеи прилагательные, а не глаголы ➢ Независимость от реализации ➢ Валидация без компиляции ➢ Не нужно уметь программировать ➢ GUI для редактирования ➢ Потому что это модно :) Декларативное программирование
  • 5.
    ➢ Декораторы ➢ Метаклассы ○Атрибуты классов ○ Аргументы классов ○ Аннотации аргументов методов ➢ Import hooks ○ Модификация AST ○ Генерация кода ➢ Внешние описания ○ YAML Декларативность в Python
  • 6.
    Декораторы @this_is_decorator(safe_mode=True) def method(arg1, arg2): #we will have just a few lines here return arg1 + arg2 @abstractclass class ObjectBase: def foo(self): return NotImplemented
  • 7.
    Метаклассы: атрибуты классов classMyClass(ObjectBase): sequence = True sorted = False seq = MyClass() seq.extend([3, 2, 5]) print(seq) > [3, 2, 5]
  • 8.
    Метаклассы: атрибуты классов classObjectMeta(type): def __new__(cls, name, bases, attrs): type_new = type.__new__(cls, name, bases, attrs) if attrs.get('sequence', False): # add sequence realization if attrs.get('sorted', False): # add sorted realization return type_new class ObjectBase(metaclass=ObjectMeta): pass class MyClass(ObjectBase): sequence = True sorted = False
  • 9.
    Метаклассы: аргументы классов classSequenceMeta(type): def __new__(cls, name, bases, attrs, sorted=False): type_new = type.__new__(cls, name, bases, attrs) if sorted: # add sorted realization return type_new class SortedSequence(metaclass=SequenceMeta, sorted=True): pass seq = SortedSequence()
  • 10.
    Метаклассы: аннотации import inspect classStrictMeta(type): def __new__(cls, name, bases, args): type_new = type.__new__(cls, name, bases, args) for attr_name in dir(type_new): method = getattr(type_new.attr_name) if callable(method): parameters = inspect.signature(method).parameters.values() # construct decorated method setattr(new_type, attr_name, method) return new_type class TextNumber(metaclass=StrictMeta): value = "0" def __add__(self, value: r'[d.]+'): # add implementation return self.value
  • 11.
    Import hooks: модификацияAST # smart_sql.py class MyImporter: def load_module(self, name): # modify AST return module sys.path_hooks.insert(0, MyImporter) # prog.py import smart_sql query = ( id, Point(x, y) for id, x, y in "sql_table_name" if len([(x0, y0), (x, y)]) < 3)
  • 12.
    Import hooks: генерациякода # smart_sql.py class MyImporter: def find_module(self, fullname, path=None): # find path to DSL source file self.path = path return self def load_module(self, name): # generate and compile python module from DSL return module sys.meta_path.insert(0, MyImporter) # prog.py import smart_sql from dsl_queries import query result = query.find(radius)
  • 13.
    YAML # pytest-yamlwsgi test_index: - path:/ assert_status: 200 assert_contains: Hello - path: / assert_contains: Hello - path: / assert_status: 200
  • 14.
    Django ORM from django.dbimport models class Musician(models.Model): first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) instrument = models.CharField(max_length=100) class Album(models.Model): artist = models.ForeignKey(Musician) name = models.CharField(max_length=100) release_date = models.DateField() num_stars = models.IntegerField()
  • 15.
    Django class-based genericviews class PublisherDetail(DetailView): context_object_name = 'publisher' queryset = Publisher.objects.all() class BookList(ListView): queryset = Book.objects.order_by('-publication_date') context_object_name = 'book_list' class AcmeBookList(ListView): context_object_name = 'book_list' queryset = Book.objects.filter(publisher__name='Acme') template_name = 'books/acme_list.html'
  • 16.
    Function annotations # http://code.activestate.com/recipes/578528/ @typecheck defhappy1(a:int, b:list, c:tuple=(1,2,3)) -> float: return 3.14 @typecheck def happy_wo_annotation(a:int, b, c:tuple=(1,2,3)) -> float: return 3.14 @typecheck def unhappy1(a:int, b:str) -> float: return 314 # This can never succeed in return type
  • 17.