Metaprogramovanie / Python

Sergej Chodarev

Metaprogramovanie 6

Metaprogramovanie v Pythone

Sergej Chodarev

Python

Duck Typing

If it looks like a duck, swims like a duck, and quacks like a duck,
then it probably is a duck.

class Dog:
    kind = 'canine'

    def __init__(self, name):
        self.name = name
        self.tricks = []

    def add_trick(self, trick):
        self.tricks.append(trick)

fido = Dog('Fido')
fido.add_trick('roll over')
print("Hello, world!")

def hello():
    print("Hello, world!")
hello()

class Hello:
    def great(self):
        print("Hello world!")
Hello().great()

Funkcie sú first-class objekty

def hello(name):
    print("Hello,", name)

def hi(name):
    print("Hi,", name)

functions = [hello, hi]

for f in functions:
    f("Sergej")

Aj triedy sú first-class objekty

class A:
    ...

class B:
    ...

classes = [A, B]
objects = []

for c in classes:
    objects.append(c())
class Hello:
    print("Hello, world!")

Čo sa stane?

Definícia triedy je imperatívna

class LineItem:
    def __init__(self, description, weight, price):
        self.description = description
        self.weight = weight
        self.price = price

    def subtotal(self):
        return self.weight * self.price
    def get_weight(self):
        return self._weight
    
    def set_weight(self, value):
        if value > 0:
            self._weight = value
        else:
            raise ValueError('value must be > 0')
    
    weight = property(get_weight, set_weight)

Introspekcia

Špeciálne atribúty

Špeciálne metódy

__init__(self, args…)   →   ClassName(args…)
__str__(self)           →   str(object)
__bool__(self)          →   bool(object)
__hash__(self)          →   hash(object)
__eq__(self, other)     →   object == other

Zmena správania

__getattr__(self, name)          →   print(object.name)
__setattr__(self, name, value)   →   object.name = value
__call__(self, args…)            →   object(args…)
__getitem__(self, key)           →   object[key]

Data model → Special method names

__why__?

Rozhranie pre ovplyvnenie jazyka

Iné jazyky

Metaobject protocol

Dekorátory

Dekorátory

@decorator
def foo(args):
    perform(some, operations)
def foo(args):
    perform(some, operations)
foo = decorator(foo)
def onexit(f):
    import atexit
    atexit.register(f)
    return f

@onexit
def func():
    ...
def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...
class LineItem:
    ...
    @property
    def weight(self):
        return self._weight

    @weight.setter
    def set_weight(self, value):
        if value > 0:
            self._weight = value
        else:
            raise ValueError('value must be > 0')

Prípadová štúdia: Flask

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"
class Flask():
    ...
    def route(self, rule, **options):
        def decorator(f):
            endpoint = options.pop('endpoint', None)
            self.add_url_rule(rule, endpoint, f, **options)
            return f
        return decorator

Metatriedy

type(fido) == Dog
type(Dog) == type

Metatriedy

class MyClass(metaclass=Meta):
    pass
class Cached(type):
    def __new__(mcs, name, bases, dct):
        dct['_cache'] = {}
        return super().__new__(mcs, name, bases, dct)
    def __call__(cls, arg):
        try:
            return cls._cache[arg]
        except KeyError:
            obj = super().__call__(arg)
            cls._cache[a] = obj
            return obj

Prípadová štúdia: Django

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    question = models.ForeignKey(
        Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
from polls.models import Choice, Question

q = Question(question_text="What's new?",
             pub_date=timezone.now())
q.save()
q.question_text = "What's up?"
q.save()
q.choice_set.create(choice_text='Not much', votes=0)
q.choice_set.create(choice_text='Just hacking again', votes=0)

qs = Question.objects.all()
qs = Question.objects.get(pub_date__year=current_year)
cs = qs[0].choice_set.all()

Pred „magic removal“

Anotácie

Typové anotácie

def greeting(name: str) -> str:
    return 'Hello ' + name

Kontrolujú len externé nástroje: mypy, PyCharm, jedi

Dataclasses

@dataclass
class InventoryItem:
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

Generuje __init__(), __eq__(), __hash__(), atď.

Zhrnieme

Notre-Dame de Paris, 4 October 2017