Python 使用向后兼容性取消勾选namedtuple(忽略其他属性)

Python 使用向后兼容性取消勾选namedtuple(忽略其他属性),python,python-3.x,namedtuple,Python,Python 3.x,Namedtuple,下面是一个场景,该场景模拟在较新版本编写的搁置数据库上运行较旧版本的Python程序。理想情况下,用户对象仍将被解析和读入;FavoritePET属性将被忽略。可以理解,它抛出了一个错误,抱怨元组不匹配 有没有一种好方法可以让这个场景与namedtuples一起工作,或者如果需要这种灵活性,最好切换到存储字典或类 import shelve from collections import namedtuple shelf = shelve.open("objectshelf", flag='n

下面是一个场景,该场景模拟在较新版本编写的搁置数据库上运行较旧版本的Python程序。理想情况下,用户对象仍将被解析和读入;FavoritePET属性将被忽略。可以理解,它抛出了一个错误,抱怨元组不匹配

有没有一种好方法可以让这个场景与namedtuples一起工作,或者如果需要这种灵活性,最好切换到存储字典或类

import shelve
from collections import namedtuple

shelf = shelve.open("objectshelf", flag='n')

User = namedtuple("User", ("username", "password", "firstname", "surname", "favouritePet"))
shelf["namedtupleAndrew"] = User("andrew@example.com", "mypassword", "Andrew", "Smith", "cat")

# Redefine User to simulate a previous version of the User object that didn't record favouritePet;
# someone using an old version of the program against a database written to by a new version
User = namedtuple("User", ("username", "password", "firstname", "surname"))
# Throws error "takes 5 positional arguments but 6 were given"
namedTupleRead = shelf["namedtupleAndrew"]

print(namedTupleRead.username)
编辑:为了完整起见,使用类也有同样的想法:

import shelve

shelf = shelve.open("objectshelf", flag='n')

class User:

    def __init__(self, username, password, firstname, surname, favouritePet):
        self.username = username
        self.password = password
        self.firstname = firstname
        self.surname = surname
        self.favouritePet = favouritePet

shelf["objectAndrew"] = User("andrew@example.com", "mypassword", "Andrew", "Smith", "cat")

# Redefine User to simulate a previous version of the User object that didn't record favouritePet;
# someone using an old version of the program against a database written to by a new version
class User:

    def __init__(self, username, password, firstname, surname):
        self.username = username
        self.password = password
        self.firstname = firstname
        self.surname = surname

objectRead = shelf["objectAndrew"]

print(objectRead.username)
# favouritePet is still there; it's just a dictionary, after all.
print(objectRead.favouritePet)

我建议使用dict或自定义类

一个命名元组需要的参数数量与它的字段数量一样多,因此要直接使用命名元组,您必须更改类“
\uuuu new\uuuu
方法,以使用
*args
**kwargs
而不是固定的参数列表。如果查看
User
类的定义(通过添加
verbose=True
参数),您将看到该类是如何定义的:

...
class User(tuple):
    'User(username, password, firstname, surname, favouritePet)'

    __slots__ = ()

    _fields = ('username', 'password', 'firstname', 'surname', 'favouritePet')

    def __new__(_cls, username, password, firstname, surname, favouritePet):
        'Create new instance of User(username, password, firstname, surname, favouritePet)'
        return _tuple.__new__(_cls, (username, password, firstname, surname, favouritePet))
...
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
与namedtuple保持一致),然后将结果序列与
tuple一起使用。\uuuu new\uuuu
。最好使用专用类,而不是以这种方式修改namedtuple的行为

通过使用协议(或)使用自定义构造函数,可以更容易地更改
用户
namedtuple的pickle方式,例如:

from collections import namedtuple
import shelve
import copyreg

shelf = shelve.open("test")

User = namedtuple("User", ("username", "password", "firstname", "surname", "favouritePet"))

User.__reduce__ = lambda user: (construct_user, tuple(user))
# or: copyreg.pickle(User, lambda user: (construct_user, tuple(user)))

def construct_user(*args):
    print('creating new user:', args)       # for debugging
    return User(*args[:len(User._fields)])


user = User("andrew@example.com", "mypassword", "Andrew", "Smith", "cat")
print(user)
shelf["namedtupleAndrew"] = user

# redefine User
User = namedtuple("User", ("username", "password", "firstname", "surname"))

print(shelf["namedtupleAndrew"])

只要
construct\u user
函数在所有兼容版本中都可用,这将起作用,但正如最初所说的,我仍然建议使用不同的数据结构。

对于类示例,是否有一种好的方法来摆脱self.username=username行,同时仍然允许在init中列出参数?