【问题标题】:Can you make classes in a loop in python?你可以在 python 中循环创建类吗?
【发布时间】:2021-04-29 11:48:10
【问题描述】:

我有一种情况,我正在制作一堆类,其中一堆实际上基本相同,所以我想将它们制作成一个循环。它们与注册系统一起使用,因此在 USAGE 方面没有问题,但我不确定如何用确定类名的变量实际定义一个类...

简单示例:

classList = ['foo', 'bar', 'baz']

for className in classList:
    class {{{className}}}_calc(BaseCalc): 
         def __init__ (self, dataFrame):
             self.column = dataFrame[className]
         def calc ():
             return self.column.sum()

显然,这是一个非常简化的案例。我无法将参数更改为 init,因为其中有一大堆已经存在,它们是更大结构的一部分。

示例的其余部分使用 pandas 语法,只是为了说明它是如何使用的……但它实际上是与 SQL DB 一起使用的,而且要复杂得多……我只是不知道想要捍卫“你为什么要这样做?”我有充分的理由,就这样吧。

classname 在 class 行的 {{{ }}} 中,表示它是一个变量,实际上在语法上并不正确。问题是“我如何表示我使用 {{{ }}} 的目的?”我想。

答案可能是元类,但我仍然不确定如何使我的类变量的名称....

ETA:尝试使用@python_user 答案:

classList = ['foooo', 'bar', 'baaz']

class Base ():
    def __init__ (self, buq):
        self.buq = buq

    def getBuq(self):
        return 'buq: ' + self.buq
    

for cls in classList:
    class TEMP(Base):
        className = cls
        def __init__ (self):
            self.qux = len(cls)
            Base.__init__(self, cls)

        def blee(self, inpt):
            return inpt+ self.qux
    TEMP.__name__ = f'{cls}'
    TEMP.__qualname__ = f'{cls}'
    globals()[cls] = TEMP
f = foo()

f.getBuq()

>>>> 'buq: baaz'

这只是给我 baaz 课程。这三个都在给 baaz... 我在做一些很愚蠢的事情吗?

【问题讨论】:

  • 使用 type() 创建一个具有动态分配名称的类,type(f"MyClass{number}", (), {})。您必须在基础中填充内容并适当地听写

标签: python python-3.x class metaclass


【解决方案1】:

你可以这样做,使用globals()

classList = ['foo', 'bar', 'baz']

for className in classList:
    class Temp:
        def __init__ (self, dataFrame):
            self.column = dataFrame[className]
        def calc ():
            return self.column.sum()
    Temp.__name__ = className
    globals()[className] = Temp

然后您可以执行foo() 来创建foo 类的对象。对于实际示例,您必须使用 __init__ 方法所需的参数进行调用。这只是为了证明它有效。

print(type(foo()).__name__) # foo
print(type(bar()).__name__) # bar
print(type(baz()).__name__) # baz

如果你想改变你的班级名称,那么你可以这样做

Temp.__name__ = f'{className}_calc'
globals()[f'{className}_calc'] = Temp

正如 wim 在 cmets 中指出的那样,您还需要设置 __qualname__Temp.__qualname__ = f'{className}_calc'

编辑:

由于类的方法中的名称查找是在运行时(而不是在编译时)执行的,所以代码 sn-p 有一个错误。 className 将始终引用classList 中的最后一个元素(在本例中为baz),此名称存在于循环范围之外,并且将是所有类的方法中className 的值。 (例如:self.column = dataFrame[className])。 dataFrame['baz']

要解决此问题,必须声明一个名为 className 的类级别变量并将 className(来自循环的变量)分配给该变量。所以在编译时这个值将绑定到类。类方法中对className 的所有引用都需要更改为self.className,代码才能按预期工作。

class Temp:
    className = className # note this line
    def __init__ (self, dataFrame):
        self.column = dataFrame[self.className] # and this line with the original snippet

【讨论】:

  • 可能是这里最不严重的解决方案。你也应该设置__qualname__
  • 我喜欢这个答案的想法,但它不起作用。我会将我的代码添加到问题文本中...
  • 好的,所以如果我用 cls=cls 替换第一行,并在任何地方使用 self.cls 而不是 cls,它就可以了!谢谢!
  • 是的,这就是我的意思,直到发布答案一个小时后才发现该错误,很高兴知道它有效
【解决方案2】:

但是,您可以使用 type 函数来创建此类动态类。 names = ['name1', 'name2'] 假设你必须在 python 中创建 10 个类对象,并对它们做一些事情,比如:obj_1 = MyClass() other_object。

希望我能帮到你。

【讨论】:

  • 我认为这可能有效,我目前正在尝试这样的事情:type(className+'calc', BaseCalc, {......}。但是字典变得非常复杂,因为它有很多多行的 lambdas ......嗯
【解决方案3】:

嗯,有一种使用“exec()”函数的复杂方法来执行此操作,该函数接受任何字符串并执行它;然后你可以将你的类构建为一个字符串并运行它。这是一个构建类的示例,您可以通过查看最后打印的全局命名空间来看到它们是声明的。

classList = ['foo', 'bar', 'baz']

class BaseCalc(): 
     def __init__ (self, dataFrame):
         self.column = dataFrame[className]
     def calc ():
         return self.column.sum()

for className in classList:
    template = '''class %s_calc(BaseCalc): 
         def __init__ (self, dataFrame):
             self.column = dataFrame[className]
         def calc ():
             return self.column.sum()''' % className
    exec(template)

print(globals())

【讨论】:

    猜你喜欢
    • 2015-12-06
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    • 2013-02-23
    • 2021-08-23
    相关资源
    最近更新 更多