【问题标题】:Nice ways to programmatically define python classes based on the cross of lists of mixins?基于混合列表的交叉以编程方式定义 python 类的好方法?
【发布时间】:2021-07-25 04:59:48
【问题描述】:

现在,我有两组 python mixin,A 和 B。

(A_1, A_2, ..., A_i, ...)(B_1, B_2, ..., B_i, ...) 分别成为这些集合中的元素。

我想创建作为这两个 mixin 集之间的交叉的类,并且能够按名称引用交叉的元素。 python中有什么好方法可以做到这一点吗?具体来说,如果说我有 mixins

class A_1():
 pass
class A_2():
 pass
class B_1():
 pass
class B_2():
 pass

我希望能够生成类:

class A_1B_1(A_1, B_1):
  pass

class A_1B_2(A_1, B_2):
  pass

class A_2B_1(A_2, B_1):
  pass

class A_2B_2(A_2, B_2):
  pass

最好的方法是什么?

另外,假设我想为集合 B 添加一个新的 mixin,命名为 B_x。有没有一种很好的方法来定义一些函数,我可以调用B_x 来生成将所有A_iB_x 交叉产生的类?即,是否有一个函数 f 我可以这样编写

f(B_x, [A_1, A_2, ..., A_n])

将导致能够生成

class A_1B_x(A_1, B_x):
  pass
class A_2B_x(A_2, B_x):
  pass

# ... and so on

值得注意的是,我希望能够在另一个文件中引用这些生成的类以按名称实例化它们。

四处挖掘,我发现了 Dynamically mixin a base class to an instance in Python 之类的东西,但它们会更改实例而不是生成类。

【问题讨论】:

    标签: python class mixins


    【解决方案1】:

    使用type(name, bases, dict),因为您基本上是在使用元类,但是以一种简化的方式。

    class X:
        pass
    
    class Y:
        pass
    
    XY = type("xy", (X, Y), {})
    
    print(XY.__mro__)
    # (<class '__main__.xy'>, <class '__main__.X'>, <class '__main__.Y'>, <class 'object'>)
    

    (__mro__ + explanation)

    将它与itertools.combinations() 结合起来动态生成所有可能的碱基(或itertools.permutations()itertools.product() 甚至):

    combinations([X, Y], 2)
    # <itertools.combinations object at 0x7fbbc2ced4a0>
    
    list(combinations([X, Y], 2))
    # [(<class '__main__.X'>, <class '__main__.Y'>)]
    

    因此有点像这样,如果您将它们放在单独的包中:

    import your_package
    classes = [
        getattr(your_package, cls)
        for cls in dir(your_package)
        if filter_somehow(getattr(your_package, cls))
    ]
    
    combined = {}
    for comb in combinations(classes, 2):
        name = "".join(cls.__name__ for cls in comb)
        combined[name] = type(name, comb, {})
    
    print(combined)
    

    对于文件外部的引用,要么将该字典导入为常量,要么直接使用globals() 而不是combined,您将在模块的命名空间中动态创建此类变量。

    对于过滤isinstance(&lt;class&gt;, type) 应该会有所帮助。

    或者您可以通过例如过滤它们__name__ attribute) 或者这样的包里没有其他东西+忽略所有dunder parts

    # empty test.py file 
    import test
    dir(test)
    # ['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']
    

    ...或为您的所有类创建一些基类,然后使用issubclass() 仅从dir(package) 获取这些。

    对于普通功能,这应该足够了:

    # f(B_x, [A_1, A_2, ..., A_n])
    def f(base, mixins):
        classes = []
        for mixin in mixins:
            bases = (mixin, base)
            name = "".join(cls.__name__ for cls in bases)
            classes.append(type(name, bases, {}))
        return classes
    
    # example
    class Base: pass
    class A: pass
    class B: pass
    class C: pass
    
    f(Base, [A,B,C])
    # [<class '__main__.ABase'>, <class '__main__.BBase'>, <class '__main__.CBase'>]
    

    让我们让它扩展一点:

    # f(B_x, [A_1, A_2, ..., A_n])
    from typing import Generic, Iterable
    from itertools import combinations
    
    def f(base: Generic, mixins: Iterable, base_count: int = 1):
        classes = []
        for mixin_comb in combinations(mixins, base_count):
            bases = (*mixin_comb, base)
            name = "".join(cls.__name__ for cls in bases)
            classes.append(type(name, bases, {}))
        return classes
    
    f(Base, [A,B,C], 2)
    # [<class '__main__.ABBase'>, <class '__main__.ACBase'>, <class '__main__.BCBase'>]
    

    【讨论】:

    • inspect.isclass(something) 是过时的。只需使用isinstance(something, type)
    • @juanpa.arrivillaga 是的,这是我的第一个想法,尽管它仍然以相同的方式工作,因为几乎所有东西都是 type 实例 (isinstance(int, type))。
    • 不确定你的意思。并非一切都是类型实例。特别是,classestype 的实例
    • 在 Python 2 中,旧式类不是 type 的实例,但在 Python 3 中,没有“旧式”与“新式”类区别,everything,每样东西都像 Python 2 “新风格”类,所有类都是 type] 的实例
    • @juanpa.arrivillaga ahaha,我的错,我刚刚注意到我选择了isinstance(int, type),即将classtype 进行比较,而不是instancetype,因此是旧的True 值。已编辑。
    猜你喜欢
    • 2020-11-21
    • 2011-07-13
    • 2013-08-26
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 2016-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多