What is the Pythonic way to map a sequence of one custom class to another?
(或者,C’s
给定一个自定义类
例如,下面的代码可以做到这一点,但这是最为Python式的方法吗?注意,实际类型有许多属性。
1 2 3 4 5 6 7 8 9 10 | l = [A('Greg', 33), A('John', 39)] def map_to_type_b(the_list): new_list = [] for item in the_list: new_list.append(B(item.name, item.age)) return new_list l2 = map_to_type_b(l) |
我来自一个C背景,在这里我将使用Linq
不仅在Python中,而且在大多数基于OO的语言中,只写数据对象是不受欢迎的。也许最为Python式的方法是传递平面数据,比如说,口述或口述列表:
1 2 3 | {'Greg': 33, 'John': 39} [{'name': 'Greg', 'age': 33}, {'name': 'John', 'age': 39}] |
也就是说,假设您有类A和类B,并且您希望从现有的A实例实例化新的B:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class A(object): def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return '<{cls} name={s.name}, age={s.age}>'.format( cls=self.__class__.__name__, s=self ) class B(A): def __init__(self, name, age, born_as='male'): super(B, self).__init__(name, age) self.born_as = born_as data = {'Greg': 33, 'John': 39} list_of_a = [A(k, v) for k, v in data.items()] |
。
你可以简单明了地说:
1 2 3 4 5 | >>> list_of_a [<A name=Greg, age=33>, <A name=John, age=39>] >>> [B(a.name, a.age) for a in list_of_a] [<B name=Greg, age=33>, <B name=John, age=39>] |
如果涉及到很多属性,这可能会变得有点冗长。让我们教B如何克隆A:
1 2 3 4 5 6 7 8 | class B(A): def __init__(self, name, age, born_as='male'): super(B, self).__init__(name, age) self.born_as = born_as @classmethod def clone(cls, instance, *args, **kwargs): return cls(instance.name, instance.age, *args, **kwargs) |
。
既然B现在知道如何克隆A:
1 2 | >>> [B.clone(a) for a in list_of_a] [<B name=Greg, age=33>, <B name=John, age=39>] |
。
为所有B类类编写克隆方法可能会变得单调乏味。反省是非常的Python,所以我们不要重复我们自己:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class CloneFromInstanceMixin(object): @classmethod def clone(cls, instance, **kwargs): constructor_args = inspect.getargspec(instance.__init__).args for attr_name in constructor_args: if attr_name in kwargs: continue # overrides instance attribute try: kwargs[attr_name] = getattr(instance, attr_name) except AttributeError: pass return cls(**kwargs) class B(CloneFromInstanceMixin, A): def __init__(self, name, age, born_as='male'): super(B, self).__init__(name, age) self.born_as = born_as >>> [B.clone(a) for a in list_of_a] [<B name=Greg, age=33>, <B name=John, age=39>] |
我可能有太多的空闲时间。
我想说,它是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class A(object): def __init__(self, name, age): self.name = name self.age = age def __repr__(self): return 'A({0.name!r}, {0.age!r})'.format(self) class B(A): def __repr__(self): return 'B({0.name!r}, {0.age!r})'.format(self) @classmethod def from_A(cls, inst): return cls(inst.name, inst.age) |
然后,您可以使用简单的列表理解,甚至使用
1 2 3 4 5 6 7 8 9 | >>> l = [A('Greg', 33), A('John', 39)] >>> l [A('Greg', 33), A('John', 39)] >>> map(B.from_A, l) # will look different, but is more memory-efficient, in 3.x [B('Greg', 33), B('John', 39)] >>> [B.from_A(a) for a in l] # works (nearly) identically in 2.x and 3.x [B('Greg', 33), B('John', 39)] |
号