2013-08-30 2 views
1

У меня есть классдекоратора, который создает классы

class Test: 
    name = "Test" 

Я хочу создать декоратор mc_me, который после того, как:

@mc_me 
class Test: 
    name = "Test" 

предоставит мне три класса так же, как если бы я написал:

class Test: 
    name = "Test" 

class TestSuper: 
    name = "TestSuper" 

class TestExtra: 
    name = "TestExtra" 

Таким образом, по сути, первая сложная часть - это глубокое копирование класса, а второе назначение трех переменных в области вызова. Кажется, что это должно быть возможно ...

ответ

3
import inspect 

def mc_me(cls): 
    main_locals = inspect.currentframe().f_back.f_locals 
    for suffix in ['Super', 'Extra']: 
     new_name = cls.__name__ + suffix 
     main_locals[new_name] = type(new_name,(), {'name': new_name}) 
    return cls 

Будьте осторожны, это не самый лучший способ кодирования. С большой властью приходит большая ответственность. Я рекомендую вам проверить все возможные недостатки подхода inspect (Google - ваш друг).

+2

Больной и красивый! :) С примечанием: «Подробности реализации CPython: эта функция опирается на поддержку фрейма стека Python в интерпретаторе, который не гарантированно существует во всех реализациях Python». [inspect.currentframe] (http://docs.python.org/2/library/inspect.html#inspect.currentframe) – neverlastn

+0

Работает только на уровне верхнего уровня или на уровне класса, и я уверен, что вы должны были return 'cls'. – user2357112

+0

Забыл 'return cls', вы правы. Я не вижу проблемы с ограничениями верхнего уровня. – gioi

0

Невозможно. Делать 3 класса легко. Однако присвоение результатов 3 переменным? Это нарушает правила области Python.

@mc_me 
class Test: 
    name = "Test" 

эквивалентно

class Test: 
    name = "Test" 
Test = mc_me(Test) 

Там нет никакого способа для декоратора, чтобы сказать, что он хочет создать еще несколько переменных и назначить их тоже. Даже если вам каким-то образом удалось заставить его работать (возможно, только с областью модуля или класса с inspect безумием), Python не мог бы знать, что ссылки на TestSuper или TestExtra должны относиться к переменным, созданным декоратором при компиляции байт-кода.

+0

Да, точно, я думал что-то вдоль линий ... '(Test, TestSuper, TestExtra) = mc_me (Test)' "Создание 3-х классов легко" - с типом? Могли бы вы глубоко скопировать и обезглавить патч? (Я знаю, это похоже на наследование). В любом случае ниже должна быть простой альтернативой класс 'Тест: имя = "Test" TestSuper = mc_me (Test, "Супер") TestExtra = mc_me (Test, "Экстра")' – neverlastn

+0

* Python будет не имеют возможности знать, что ссылки на TestSuper или TestExtra должны ссылаться на переменные, созданные декоратором при компиляции байт-кода. * Конечно? Bytecode не будет включать вновь созданные 'TestSuper' и' TestExtra', но инструкции для их создания. На самом деле это работает. – gioi

0

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

# Library code 
def mc_me(base): 
    class Sp(base): 
     name = base.name + "Super" 

    class Ex(base): 
     name = base.name + "Extra" 

    return (Sp, Ex) 

# Application code 
class Test: 
    name = "Test" 

(TestSuper, TestExtra) = mc_me(Test)