2016-12-19 2 views
0

Мне нужно преобразовать некоторый код из python 3 в python 2. У меня есть метакласс, где метод __prepare__ устанавливает функцию в классе dict. Я попытался перевести на метод __new__, но я не могу настроить функцию SET_DEFAULTS. Это возможно ?Как заменить __prepare__ для метакласса в python 2

У меня есть NameError: name 'SET_DEFAULTS' при инициализации

class UazeMessageMeta (type): 

    @staticmethod 
    def __prepare__(name, bases, **kwargs): 
     d = {} 
     for b in bases: 
      if 'DEFAULT_VALUES' in dir(b): 
       d.update(b.DEFAULT_VALUES) 
     return { 
      'SET_DEFAULTS' : lambda **kwargs : d.update(kwargs), 
      'DEFAULT_VALUES' : d 
     } 

class UazeMessage (bytearray): 
    """ 
    A basic message (header only). This class also provides the 
    base behavior for all messages. 
    """ 
    # gp test py27 ----------------- 
    __metaclass__ = UazeMessageMeta 

    # ------------ 

    priority = MessageField(0, 1, Priority) 
    sequence = MessageField(1, 7, FieldType.UNSIGNED) 
    readWrite = MessageField(8, 1, ReadWriteFlag) 
    ack   = MessageField(9, 2, Ack) 
    channel  = MessageField(11, 2, FieldType.UNSIGNED) 
    category = MessageField(13, 3, Category) 
    item  = MessageField(16, 8, FieldType.UNSIGNED) 

    DEFAULT_SIZE = 3 

    def __init__(self, init=0, setdefaults=None, **kwargs): 
     # If init is still or None, initialize the size of the message 
     # using the default size provided in the class. 
     if init == None or init == 0: 
      init = type(self).DEFAULT_SIZE 
     super(UrmpMessage,self).__init__(init) 
     # Set any default or provided fields. 
     initval = {} 
     if (isinstance(init, int) and setdefaults != False) or \ 
      (setdefaults == True): 
      initval = dict(self.DEFAULT_VALUES) 
     initval.update(kwargs) 
     for key, value in initval.items(): 
      setattr(self, key, value) 

class ResetBase (UazeMessage): 
    """Reset response/request structure.""" 

    resetType = MessageField(24, 8, ResetType) 

    SET_DEFAULTS(
     category = Category.OPERATION, 
     resetType = ResetType.SOFT, 
     item  = 0) 

    DEFAULT_SIZE = 4 
+1

Просмотрите отступ в своем посте; это важно в Python. – jonrsharpe

+0

Я обновил, извините за неудобства – WatchdogReset

+0

Кажется, что по крайней мере один класс отсутствует. Пожалуйста, дайте [mcve]. – jonrsharpe

ответ

1

Как правило, вы не можете сделать это. Введение __prepare__ является фундаментальным изменением в Python3, что позволяет настраивать пространство имен, в котором анализируется тело класса.

Я считаю, что главная мотивация делать это должны было обеспечить способ заменить локальное пространство имен внутри тела класса с OrderedDict, так что инициализация класса (в метаклассе __new__ или __init__ методов), может извлечь выгоду из того, деклараций методы и атрибуты внутри тела класса. Следует учитывать, что с Python 3.6 (окончательная версия на этой неделе) в корпусе класса по умолчанию используется упорядоченный словарь, и метаклас больше не нужен для этого.

Механизм __prepare__ более гибкий, чем это, и в более простом использовании позволяет просто предварительно заполнить словарь класса тела заданными значениями. Это то, что делает ваш проект.

Однако, поскольку этот код не требует специального словарного класса и просто предварительно заполняет обычный словарь, все, что вам нужно сделать, это написать обычную функцию, которая использует в качестве параметров словарь и базовые классы и заполняет в этом словаре в соответствии с существующим кодом в методе __prepare__. Затем вызовите эту функцию в начале тела класса, передав в словаре значение, возвращаемое параметром locals(). То есть это: пространство имен класса тела может быть предварительно заполнено в порядке.

def prepare(bases, dct): 
    for base in bases: 
     dct["special_attribute"] = {} 
     if "special_attribute" in base.__dict__: 
      dct["special_attribute" ].update(base.__dict__["special_attribute"]) 
    ... 

class MyClass(bytearray): 
    prepare((bytearray,), locals()) 
    ... 

Все, что сказал, я действительно советую вам попробовать, если это возможно, чтобы НЕ портировать проект к python2 в данный момент времени - это только усложнит кодовую - и отказаться от использования новых функций на постоянной основе (например, этот наконечник выше, а не __prepare__)

+0

Спасибо за ваше объяснение - это было то, что я искал. Я попытаюсь сделать проект совместимым с python 2 и 3, но это может быть сложно, когда дело доходит до метакласса. – WatchdogReset

+0

Следует отметить, что словарь по умолчанию, который заказывается в Python 3.6, не обязательно будет истинным для Python 3.6+ * все же*. Официальные заявления - это аккуратная функция, которая не обязательно будет поддерживаться в будущем. В других словах, программист Остерегайтесь. – KronoS

+0

Хотя заказ по умолчанию для литерального dict все еще обсуждается, упорядочение внутри тела класса является отдельной проблемой (хотя на уровне реализации они смешиваются). Заказ в объявлении атрибутов класса определяется в PEP 520 и теперь является частью спецификации языка. – jsbueno

Смежные вопросы