将一个自定义类的序列映射到另一个自定义类的 Pythonic 方法是什么? [英] What is the Pythonic way to map a sequence of one custom class to another?

查看:34
本文介绍了将一个自定义类的序列映射到另一个自定义类的 Pythonic 方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(...或者,C# 的 Select(...) 方法的 Pythonic 版本是什么?)

给定自定义类 A 的列表 l,将 l 的每个元素映射到不同的自定义类 B?

例如,下面的代码会做到这一点,但这是最 Pythonic 的方法吗?注意,真正的类型有很多属性.

l = [A('Greg', 33), A('John', 39)]def map_to_type_b(the_list):新列表 = []对于 the_list 中的项目:new_list.append(B(item.name, item.age))返回新列表l2 = map_to_type_b(l)

我来自 C# 背景,我将使用 LinQ selectSelect() 扩展方法从源序列投影到新序列输入B.

解决方案

编写纯数据对象不仅在 Python 中而且在大多数基于 OO 的语言中都是不受欢迎的.可能最 Pythonic 的方式是传递平面数据,比如一个字典或字典列表:

{'Greg': 33, 'John': 39}[{'name': 'Greg', 'age': 33}, {'name': 'John', 'age': 39}]

也就是说,假设您有类 A 和 B,并且您想从现有的 A 实例实例化新的 B:

A 类(对象):def __init__(自我,姓名,年龄):self.name = 姓名self.age = 年龄def __repr__(self):return '<{cls} name={s.name}, age={s.age}>'.format(cls=self.__class__.__name__,s=自己)B(A)类:def __init__(self, name, age,born_as='male'):super(B, self).__init__(name, age)self.born_as =born_as数据 = {'格雷格':33,'约翰':39}list_of_a = [A(k, v) for k, v in data.items()]

你可以保持简单和明确:

<预><代码>>>>list_of_a[<姓名=格雷格,年龄=33>,<姓名=约翰,年龄=39>]>>>[B(a.name, a.age) for a in list_of_a][<B 姓名=格雷格,年龄=33>,<B 姓名=约翰,年龄=39>]

如果涉及很多属性,这可能会变得有点冗长.让我们教 B 如何克隆 A:

B(A)类:def __init__(self, name, age,born_as='male'):super(B, self).__init__(name, age)self.born_as =born_as@类方法def clone(cls, instance, *args, **kwargs):返回 cls(instance.name, instance.age, *args, **kwargs)

既然 B 现在知道如何克隆 A:

<预><代码>>>>[B.clone(a) for a in list_of_a][<B 姓名=格雷格,年龄=33>,<B 姓名=约翰,年龄=39>]

为所有类似 B 的类编写克隆方法会变得乏味.自省是非常 Pythonic 的,所以让我们不要重复自己:

class CloneFromInstanceMixin(object):@类方法def克隆(cls,实例,**kwargs):constructor_args = inspect.getargspec(instance.__init__).args对于 constructor_args 中的 attr_name:如果 attr_name 在 kwargs 中:continue # 覆盖实例属性尝试:kwargs[attr_name] = getattr(instance, attr_name)除了属性错误:经过返回 cls(**kwargs)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 姓名=格雷格,年龄=33>,<B 姓名=约翰,年龄=39>]

我可能有太多空闲时间.

(...or, alternatively, what is the Pythonic version of C#'s Select(...) method? )

Given a list l of a custom class A what is the (most?) Pythonic way to map each element of l to a different custom class B?

for example, the following code will do it, but is it the most Pythonic way of doing it? Note, the real types have many properties.

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)

I'm coming from a C# background, where I would use LinQ select or the Select() extensions method to project from the source sequence to a new sequence of type B.

解决方案

Writing data-only objects is frowned upon not only in Python but in most OO-based languages. Probably the most Pythonic way would be pass flat data around, lets say, a dict or list of dicts:

{'Greg': 33, 'John': 39}

[{'name': 'Greg', 'age': 33}, {'name': 'John', 'age': 39}]

That said, suppose you have classes A and B and you want to instantiate new Bs from existing A instances:

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()]

You can keep it simple and just be explicit:

>>> 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>]

If there are a lot of attributes involved, this can get a bit verbose. Lets teach B how to clone an A:

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)

Since B now knows how to clone A:

>>> [B.clone(a) for a in list_of_a]
[<B name=Greg, age=33>, <B name=John, age=39>]        

It can get tedious to write clone methods for all B-like classes. Introspection is very Pythonic, so lets not repeat ourselves:

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>]   

I probably have too much free time.

这篇关于将一个自定义类的序列映射到另一个自定义类的 Pythonic 方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆