Метапрограмиране

„ Програмиране с Python“, ФМИ

2010/05/10

От предната лекция

Meyer's substitution uniform access principle

Атрибутите на един обект трябва да бъдат достъпвани през хомогенна нотация, която не издава дали те се изчисляват или са записани.

Още малко: LSP

Liskov substitution principle:

Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.

LSP: на Български

Ако S е подтип на T, и q(t) свойство, което важи за всеки обект от тип T, тогава q(t) трябва да важи за всеки обект от тип S.

Що е метапрограмиране?

Meta (from Greek: after, beyound, with) is a prefix used in English in order to indicate a concept which is an abstraction from another concept, used to complete or add to the latter.

Типични примери

magick: py.test

def func(x):
    return x + 1
def test_answer():
    assert func(3) == 5

Къде е магията?

magick: py.test

=========================== test session starts ============================
python: platform linux2 -- Python 2.6.2 -- pytest-1.1.0
test object 1: test_sample.py

test_sample.py F

================================= FAILURES =================================
_______________________________ test_answer ________________________________

    def test_answer():
>       assert func(3) == 5
E       assert 4 == 5
E        +  where 4 = func(3)

test_sample.py:6: AssertionError
========================= 1 failed in 0.08 seconds =========================

magick: djvu (ORM)

from dejavu import Unit,UnitProperty,storage
import os
import dejavu

class Zoo(Unit):
    Name = UnitProperty()
    Size = UnitProperty(int)
    
    def total_legs(self):
        return sum([x.Legs for x in self.Animal()])
        # вместо Пайтън да ви сметне сумата, този ред
        # генерира SQL код и го пуска към базата данни

Магия: Design by contract

from DbC import DbC

class Test(metaclass = DbC):
    
    @invar(x >= 0)
    
    def __init__(self, x):
        self.x = x

    @pre(x >= 0)
    @post(res == x + 1)
    def foo(self, x):
        result = x + self.x
        self.x -= 2
        return result

    @staticmethod
    @pre(len(list(map(lambda x: x + 2, filter(lambda y: y == 0, z + [0])))) == 1)
    def bar(z):
        pass

t = Test(1)
t.foo(3)
Test.bar([1, 2, 3])

Магия: Multiple dispatch

rom multi import Multi

class Bar(metaclass = Multi):
    def what(self, y : str, z : int, x : str): return 5
    def what(self, y : object, z : str, x : str = "abc"): return x
    def what(self, y : str, z : str, x : str): return x + y + z

b = Bar()
assert b.what("b", x = "a", z = "c") == "abc"
assert b.what(z = "a", y = 8) == "abc"
assert b.what(z = "a", x = "b", y = 1) == "b"

Data Model (aka MOP)

Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects.

Обектите отвътре

class Person:
    def __init__(self, name):
        self.name = name

    def say_hi(self):
        print("Hi, I am", self.name)

Обектите отвътре

Обектите отвътре са просто речници. Всеки обект си има специален речник който пази атрибутите му:

>>> goshko = Person('Gospodin Goshko')
>>> hasattr(goshko, '__dict__')
True
>>> goshko.__dict__
{'name': 'Gospodin Goshko'}

>>> goshko.__dict__['profession'] = 'Hacker'
>>> goshko.profession
'Hacker'
>>> goshko.__dict__
{'profession': 'Hacker', 'name': 'Commander Gosh'}

>>> goshko.__dict__.clear()
>>> goshko.name
Traceback (most recent call last):
  File "", line 1, in 
AttributeError: 'Person' object has no attribute 'name'

Класът

class Ninja:
    def __init__(self, name, target):
        self.name = name
        self.target = target

    def say_hi(self):
        print("Ninja!")

    def kill_target(self):
        print("Slash ", self.target)

Класът е специален атрибут на обекта

>>> goshko = Person('Gospodin Goshko')
>>> goshko.say_hi()
Hi, I am Gospodin Goshko
>>> type(goshko)
<class '__main__.Person'>
>>> goshko.__class__
<class '__main__.Person'>

>>> goshko.__class__ = Ninja
>>> type(goshko)

>>> goshko.say_hi()
Ninja!
>>> goshko.kill_target()
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 4, in kill_target
AttributeError: 'Ninja' object has no attribute 'target'

Основни методи

object.__new__(cls[, ...]) # class method

object.__init__(self[, ...])

object.__del__(self)

__new__

__new__ е истинският конструктор на вашите обекти. __init__ е само инициализатор:

class Vector(tuple):

    def __new__(klass, x, y):
        return tuple.__new__(klass, (x, y))

    def __add__(self, other):
        if not isinstance(other, Vector):
            return NotImplemented
        return Vector(self[0] + other[0], self[1] + other[1])

Достъп на атрибути

object.__getattr__(self, name)

object.__setattr__(self, name, value)

object.__delattr__(self, name)

object.__dir__(self)

Още достъп на атрибути

object.__getattribute__(self, name)

При извикване на obj.name:

  1. проверява се дали name не присъства в obj.__dict__. Ако да - връща се тази стойност
  2. ако не - проверява се дали класът, obj.__class__ има такъв атрибут в своя __dict__. Ако да, и той няма метод __get__, се връща
  3. ако атрибута на obj.__dict__ има метод __get__, то методът obj.__dict__.__get__ се изпълнява със съответните оргументи

Дескриптри

object.__get__(self, instance, owner)

object.__set__(self, instance, value)

object.__delete__(self, instance)

Извикване на дескриптори

# direct call
x.__get__(a)

# instance binding on a.x
type(a).__dict__['x'].__get__(a,  type(a))

# class binding on A.x
A.__dict__['x'].__get__(None,  A)

# super binding
# super black magic

Функции, методи и пропъртита

Връзки

Python Data Model:

Още въпроси?