Monthly Archives: Вересень 2012

Моє XVideo позеленіло :(

Вчора не міг заснути. Мучило невирішене питання. Яким чином mplayer може рендерити купу fps в повноекранному режимі, встигаючи при цьому читати з диска і робити декодинг. В такому разі я повинен могти отримати ще більше fps при рендерингу якоїсь функції виду

RGB video(int x, int y, int frame);

Зранку встав і почитав про mplayer -vo help. Помилувався драйвером matrixview.

Почитав про X Video, там знайшов посилання на демо-код, і вирішив спробувати скомпілювати: “gcc -o testxv testxv.c -L/usr/X11R6/lib -lX11 -lXext -lXv“. Вийшло не зразу. Після встановлення пакетів libx11-dev, x11proto-video-dev, libxcb-xv0-dev, libxv-dev.

Скомпілювалось, і почав гратись. Чомусь ніяк не міг отримати ніякого кольору крім зеленого і чорного. Навіть написав функцію перетворення кольорової моделі з моєї улюбленої RGB в якусь незрозумілу YUV.

Може неправильно?

Нічого. А ще гірше, що коли змінив роздільну здатність, то fps впали до 10-15.

Отака пічалька. :(

Може використати SDL? Напевне я спробую SDL. Там hello world точно коротший.

P.S. Поки навіть не знаю що я хочу намалювати. Може потенціал електричного поля в системі з кількома рухомими зарядами, або анімацію якогось фрактальчика, але руки сверблять просто зробити і знати що я це можу.


Filed under: Графіка, Кодерство Tagged: С++, linux

Каррінг в Python

Увага, тут мої коментатори пишуть що я не розбираюсь в термінах. Це правда, і це потрібно мати на увазі.

Каррінг – це коли ви передаєте в функцію не всі параметри які потрібні їй для того щоб обчислити результат, а вона замість того щоб матюкатись повертає вам іншу функцію яка може обчислити результат коли їй передати решту параметрів (або якщо їй знову передали не все що потрібно – повернути ще одну функцію).

В Python функції – об’єкти першого роду, і є замикання, тому це можливо. А ще можна буде використати рекурсивний декоратор. ;)

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

def my_sum(*args):
    return sum(args)

І зробимо так щоб її можна було викликати отак:

5 == my_sum(1, 1)(1)(1, 1)()

Останні дужки суто для того щоб було ясно, коли припиняти повертати функцію, і нарешті обчислити результат. Не буду довго вас мучити, і на наступній сторінці одразу напишу як це робиться. А поки що можете не перегортати і придумати самі.


Filed under: Кодерство Tagged: Python

Метакласи для чайників

Якщо ви чайник, то вам метакласи непотрібні, і навіть не бажані. В 99% випадків звичайно можна обійтись без них. Але їх варто знати хоча б для того щоб перестати бути чайником.

Це важко, якщо неможливо знайти застосування. Коли я на одній “п’янці” спитав для чого потрібні метакласи Костя сказав мені що напкриклад для того щоб обчислити len() від класу. Ну, а я як людина яка про аналіз вимог не просто чула, а й робила доповідь на 4 курсі, логічно запитав “Для чого потрібно обчислювати len() від класу?”. Відповідь невідома. Та ми тут зібрались не для того щоб виясняти для чого це потрібно, а щоб розібратись що це таке взагалі. Тому давайте напишемо клас, len() повертає нам кількість його екземплярів. Просто для розваги.

В Python – все об’єкт. Кожен об’єкт має тип, який можна отримати функцією type(). Наприклад:

>>> type(1)
<type 'int'>
>>> type(None)
<type 'NoneType'>
>>> import sys
>>> type(sys)
<type 'module'>
>>> type(type(1))
<type 'type'>

Цікаво що в Python3, ці типи вже є класами:

>>> type(1)
<class 'int'>
>>> type(None)
<class 'NoneType'>
>>> import sys
>>> type(sys)
<class 'module'>
>>> type(type(1))
<class 'type'>

Це трохи збиває з пантелику, але насправді класи і типи це одне й те саме. В старіших версія Python були якісь відмінності, там старі класи це класи, а нові класи – це типи:

>>> class Old:
...     pass
... 
>>> class New(object):
...     pass
... 
>>> type(Old)
<type 'classobj'>
>>> type(New)
<type 'type'>

Але зараз прийнято писати все новими класами і забути про відмінності. :)

Ах. Ви бачили що ми можемо отримати тип типу? Тип типу це те ж саме що й клас класу, тобто метаклас. В Python все – об’єкти, класи в тому числі. Об’єкти – екземпляри класів, класи – екземпляри метакласів.

Для класів в Python також характерне те, що вони всі – об’єкти яких можна викликати, і їх виклик створює нам новий екземпляр. Наприклад отримати рядок можна викликавши клас str():

>>> str(1)
'1'
>>> type('')(1)
'1'

А можна викликати той клас що нам повертає type(''), і теж отримати рядок. Це той самий клас.

Тепер, класи можна створювати так само як і інші об’єкти, викликом конструктора. Тільки цього разу метакласу. Як називається цей конструктор? Ну, ми можемо й не знати, а використати функцію type():

>>> type(type(1))('')
<type 'str'>
>>> type(type(1)) == type
True

Хах, виявляється сама type() і є класом класів. Тільки як з її допомогою сконструювати клас не маючи його екземплярів? Бо ж зазвичай спочатку є клас, а потім вже його екземпляри. Давайте добре почитаємо документацію. type() з трьома параметрами – конструктор. І він дозволяє нам створювати типи прямо в виразах, на льоту. Прямо як лямбда-функція функції на льоту. Лямбда-клас, ага.

Що ми передаємо в конструктор? name – рядок з ім’ям того що створюємо, bases – кортеж класів від яких наш буде успадковувати атрибути і словник власних атрибутів.

Таким чином наступні два способи створення класу еквівалентні:

>>> class X(object):
...     a = 1
...
>>> X = type('X', (object,), dict(a=1))

Тепер варто задати собі питання “Чи існують метакласи окрім type()?”. І на щастя відповідь – так, існують. Ми можемо наприклад пронаслідуватись від type(). Давайте так і зробимо і почнемо писати наш клас, довжина якого – кількість екземплярів.

class CountedInstancesMeta(type):
    def __len__(self):
        return self._len

Отак. Це клас що наслідується від type, і описує магічний метод __len__, який буде викликатись коли від екземпляра спробують обчислити len(). Сам екзепляр буде передаватись в self, як і в звичайних класах, ну а довжину ми сховаємо в атрибуті.

Ну що, давайте тепер збацаємо клас певної довжини?

>>> ExampleClass = CountedInstancesMeta('ExampleClass', (object, ), {'_len': 42})
>>> ExampleClass()
<__main__.ExampleClass object at 0x8eaaa0c>
>>> len(ExampleClass)
42
>>> type(ExampleClass)
<class '__main__.CountedInstancesMeta'>

Бачимо що довжина працює, ми її задали як 42. Також типом нашого класу є не type(), а наш метаклас.

Але як зробити клас, довжина якого – кількість екземплярів? Тут “лямбда” формою створення буде незручно оперувати, тому використаємо інший синтаксис.

class CountedInstances(metaclass=CountedInstancesMeta):
    __metaclass__ = CountedInstancesMeta
    _len = 0

    def __init__(self):
        self.__class__._len += 1
    def __del__(self):
        self.__class__._len -= 1

Код який ви бачите вище – на Python3. Для того щоб задати метаклас використовується синтаксис подібний до задання стандартних значень аргументів функції. В Python2 використовувався магічний атрибут __metaclass__, який Python3 успішно ігнорує.

Далі теж просто, достатньо лише знати різницю між атрибутом класу, та атрибутом екземпляра. При створенні нового екземпляра ми збільшуємо значення атрибуту класу, при його видаленні – зменшуємо. Щоб добратись до атрибуту класу, треба мати клас. Щоб добратись до класу – взяти тип екземпляра, або його атрибут __class__.

Тест:

print(len(CountedInstances)) # 0
x = CountedInstances()
print(len(CountedInstances)) # 1
y = CountedInstances()
print(len(CountedInstances)) # 2
y = x
print(len(CountedInstances)) # 1
del x
print(len(CountedInstances)) # 1
del y
print(len(CountedInstances)) # 0

Особливо цікаво почути коментарі тих, хто справді придумав куди б ці метакласи застосувати.


Filed under: Кодерство Tagged: Python

Виклик метода класу з доступом до простору імен класу при побудові класу в Python

Ось нарешті можливість застосувати метакласи! ;)

Власне те що мені було потрібно – це трошки скоротити код ініціалізації атрибутів класу, написавши функцію яка використовуючи вже ініціалізовані атрибути обчислює за ними нові. Справжній код я замінюю еквівалентим, але таки що демонструє пошук рішення:

class SelfUnconscious(object):
    knowledge = dict(
        life=42,
    )
    def reason(about):
        return knowledge.get(about, "don't know about %s" % about)

    sense = reason('life')

#Traceback (most recent call last):
#  File "class_scope.py", line 1, in <module>
#    class SelfUnconscious(object):
#  File "class_scope.py", line 8, in SelfUnconscious
#    sense = reason('life')
#  File "class_scope.py", line 6, in reason
#    return knowledge.get(about, "don't know about %s" % about)
#NameError: global name 'knowledge' is not defined

Хм, метод класу не бачить простір імен в який сам і попадає. Спробуємо передати як параметр.

class SelfUnconscious(object):
    knowledge = dict(
        life=42,
    )
    @classmethod
    def reason(cls, about):
        return cls.knowledge.get(about, "don't know about %s" % about)

    sense = reason('life')

# Traceback (most recent call last):
#   File "class_scope.py", line 1, in <module>
#     class SelfUnconscious(object):
#   File "class_scope.py", line 30, in SelfUnconscious
#     sense = reason('life')
# TypeError: 'classmethod' object is not callable

Хм, не можна викликати методи класу до того як буде завершено побудову цього класу. Шкода. При побудові їх все одно видно, але це зовсім і не методи. Цікаво то як…

class SelfUnconscious(object):
    knowledge = dict(
        life=42,
    )
    @classmethod
    def reason(cls, about):
        return SelfUnconscious.knowledge.get(about, "don't know about %s" % about)

    sense = SelfUnconscious.reason('life')

# Traceback (most recent call last):
#   File "class_scope.py", line 1, in <module>
#     class SelfUnconscious(object):
#   File "class_scope.py", line 45, in SelfUnconscious
#     sense = SelfUnconscious.reason('life')
# NameError: name 'SelfUnconscious' is not defined

Знову ж таки, немає ще цього класу.

Ну, можна передати параметри явно, не морочачи собі голову з просторами імен. Це не складно.

class SelfUnconscious(object):
    knowledge = dict(
        life=42,
    )
    def reason(about, knowledge):
        return knowledge.get(about, "don't know about %s" % about)

    sense = reason('life', knowledge)

print(SelfUnconscious.sense)
# 42

Складніше коли атрибутів багато, і хочеться доступу до всіх. В такому разі таки можна використати метакласи. Метакласи це просто – це об’єкти, екземплярами яких є класи. Відповідно виклик цього об’єкту поверне нам клас. Можна почати з функції:


def run_init_method(cls):
    cls.class_init()
    return cls

@run_init_method
class SelfUnconscious(object):

    @classmethod
    def reason(cls, about):
        return cls.knowledge.get(about, "don't know about %s" % about)

    @classmethod
    def class_init(cls):
        cls.knowledge = dict(
            life=42,
        )
        cls.sense = cls.reason('life')

print(SelfUnconscious.sense)
# 42

Ах, все готово, метакласи таки справді в 99% випадків не потрібні. На роботі взагалі обійшовся явною передачею всіх атрибутів в мето. Але колись прийде час і я таки знатиму Python до кінця.


Filed under: Кодерство Tagged: Python