python - Unpickle namedtuple with backwards compatibility (ignoring additional attributes) -
here's scenario simulates running older version of python program against shelve database written newer version. ideally, user object still parsed , read in; favouritepet attribute ignored. understandably, throws error complaining tuple doesn't match.
is there way of making scenario work namedtuples, or better switch storing dictionary or class if sort of flexibility required?
import shelve 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 simulate previous version of user object didn't record favouritepet; # using old version of program against database written new version user = namedtuple("user", ("username", "password", "firstname", "surname")) # throws error "takes 5 positional arguments 6 given" namedtupleread = shelf["namedtupleandrew"] print(namedtupleread.username)
edit: completeness here's same idea using class:
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 simulate previous version of user object didn't record favouritepet; # using old version of program against database written 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 still there; it's dictionary, after all. print(objectread.favouritepet)
i advise use dict or custom class.
a named tuple needs many arguments has fields, make work named tuple directly yout'd have change class' __new__
method use *args
, **kwargs
instead of fixed list of arguments. if have @ definition of user
class (by adding verbose=true
argument), you'll see how class defined:
... 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)) ...
__new__
have become __new__(_cls, *args, **kwargs)
, , correctly parse args
, kwargs
(you'll still want able use user('a', 'b', 'c', ...)
user('a', password='b', firstname='c', ...)
not user('a', username='a', ...)
remain consistent namedtuple), , use resulting sequence tuple.__new__
. it's better use dedicated class instead of modifying behaviour of namedtuple in such way.
it easier change way user
namedtuple pickled using __reduce__
protocol (or copyreg.pickle()
) using custom constructor function, e.g:
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) # 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"])
this work long construct_user
function available in compatible versions, said initially, still recommend use different data structure.
Comments
Post a Comment