2017-02-01 7 views
1

У меня есть порт унаследованного кода (~ 60K LOC) из Python 2 до 3, который имеет несколько тысяч структур, как показано ниже:Как порт `__slots__` из Python 2 до 3

class Sample(object): 
    __slots__ = ('both', 'advertise') 
    class __metaclass__(type): 
     __instancecheck__ = classmethod(
       lambda cls, inst: inst in ('both', 'advertise')) 
    both = 'both' 
    advertise = 'advertise' 

Этот код прекрасно работает с Python 2, но не компилируется с Python 3 и решить его нужно изменить его

class Sample(object): 
    __slots__ = ('both', 'advertise') 
    class __metaclass__(type): 
     __instancecheck__ = classmethod(
       lambda cls, inst: inst in ('both', 'advertise')) 
    def __init__(self): 
     both = 'both' 
     advertise = 'advertise' 

что бы быть эффективным способом, чтобы справиться с этим изменения, учитывая, что это должно быть сделано в течение такой большой файл несколько раз?

Мы должны учитывать, что определение класса может быть или не быть уже __init__ для класса, а также могут быть вложенные определения классов.

Это то, что я пробовал до сих пор.

  • 2to3 не признает это как проблему и, следовательно, не меняет его.
  • Одним из возможных способов может быть использование модуля ast для изменения дерева разбора в памяти, а затем используйте unparse для записи измененного кода. Но это не просто.
  • Если ничего не работает, я рассмотрю возможность написания простого сценария Shell/Python, который будет читать исходный файл, вносить изменения и записывать его обратно.

Может ли быть другой быстрый и простой способ справиться с этим изменением.

+1

[Метаклассы изменились в Python 3] (https://docs.python.org/3/reference/datamodel.html), '__metaclass__' Безразлично Больше не работай. Вы также можете заменить метаклассы (по одному для каждого класса здесь) одним общим метаклассом и использовать 'lambda cls, inst: inst в cls .__ slots __)', который должен работать для всех классов. –

+1

И вы также можете заменить код в '__init__'' на слот в __slots__: setattr (self, slot, slot) ' –

ответ

2

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

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

Такой завод может выглядеть следующим образом:

class HasStringInstances(type): 
    def __instancecheck__(cls, instance): 
     return instance in cls.__slots__ 

def create_special_class(*slots): 
    class SpecialClass(object, metaclass=HasStringInstances): 
     __slots__ = slots 

     def __init__(self): 
      for slot in self.__slots__: 
       # assign name as value 
       setattr(self, slot, slot) 

    return SpecialClass 

Sample = create_special_class('both', 'advertise') 
+0

Спасибо. Это звучит как хорошая обратная связь. Да, «ast» казался слишком тяжелым для этого. Кроме того, узлы в классе 'ast.NodeVisitor' не имеют указателя/ссылки на родителя, поэтому было трудно перемещать узлы в дереве разбора. – ViFI