简体   繁体   中英

How to port `__slots__` from Python 2 to 3

I have to port a legacy code ( ~60K LOC) from Python 2 to 3 which has a couple of thousand of structures like below:

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

This code works fine with Python 2 but doesn't compile with Python 3 and to resolve it I need to change it to

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'

What would be an efficient way to handle this change given that it has to be done over such a large file multiple times?

We have to consider that there may or may not be a __init__ function definition already for the class and there can be nested class definitions as well.

This is what I have tried so far.

  • 2to3 doesn't recognize this as an issue and hence doesn't change it.
  • One possible way could be to use ast module to modify the parse tree in memory and then use unparse to write back the modified code. But it is not straightforward.
  • If nothing else works, I will consider writing a simple Shell/Python script which will read the source file, make changes and write it back.

Can there be another quick and simple way to handle this change.

I don't know of any better way than to write a small script. I think the changes are small enough that you can get away with some nice heuristics and don't need the full power of ast .

However, if you have this much repetition of code, I would remove the classes from your code altogether. You can replace them with either a code generator or write a factory function for these classes. This is going to future-proof your code for any changes.

Such a factory could look like this:

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')

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM