Как перенести `__slots__` из Python 2 в 3

Я должен портировать старый код ( ~60K LOC) из 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 ответ

Решение

Я не знаю лучшего способа, чем написать небольшой сценарий. Я думаю, что изменения настолько малы, что вы можете избавиться от приятной эвристики и не нуждаться в полной мощности 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')
Другие вопросы по тегам