【问题标题】:dynamically create a type [duplicate]动态创建类型[重复]
【发布时间】:2014-01-19 01:13:48
【问题描述】:

我可以动态创建类定义,就像那里:

class_name = 'Human'
base_classes = (object,)
attributes = {'name':'',
              'books':list(),
              'say_hello':lambda self: sys.stdout.write('Hello!')}

Human = type(class_name, base_classes, attributes)

uzumaxy = Human()
uzumaxy.name = 'Maxim'
uzumaxy.books.append('Programming via .NET')
print(uzumaxy.name)  # Out: "Maxim"
print(uzumaxy.books) # Out: "['Programming via .NET']"

grandrey = Human()
grandrey.name = 'Andrey'
grandrey.books.append('Programming via python')

print(grandrey.name)  # Out: "Andrey"
print(uzumaxy.name)  # Out: "Maxim"

print(grandrey.books)  # Out: "['Programming via .NET', 'Programming via python']"
print(uzumaxy.books)  # Out: "['Programming via .NET', 'Programming via python']", but i'm expecting: "['Programming via .NET']"

似乎,属性“name”是实例级别的,但为什么属性“books”是类级别的? 如何使用实例级属性动态创建类型定义?谢谢帮忙。

【问题讨论】:

  • @MartijnPieters,问题是一样的,但我使用元编程,所以这些是不同的问题。
  • 不,问题完全一样。
  • 如何生成类并不重要;解决办法还是一样的;如果您需要实例属性,请不要使用可变类属性。

标签: python metaprogramming


【解决方案1】:

实际上,namebooks 都是类级别的。只是字符串是不可变的,所以当你使用uzumaxy.name = "Maxim" 时,你添加了一个名为name 的新属性,隐藏了name 类,而对于uzumaxy.books.append("Programming via .NET"),你正在访问现有的(类)@987654327 @ 并修改它。你的代码相当于这个:

class Human(object):
    name = ''
    books = []

    def say_hello(self):
        sys.stdout.write("Hello!")

注意相同的行为。传统上,我们会通过这样写Human 来解决这个问题:

class Human(object):
    def __init__(self):
        self.name = ''
        self.books = []

    def say_hello(self):
        sys.stdout.write("Hello!")

现在每个实例都有自己的namebooks。要使用动态创建的类型执行此操作,您基本上做同样的事情,给它一个__init__

def init_human(self):
    self.name = ''
    self.books = []

attributes = { '__init__': init_human,
               'say_hello': lambda self: sys.stdout.write("Hello!") }

【讨论】:

  • 当我动态收集属性(将在运行时更改)并且无法编写预定义函数“init_human”时,我必须做什么?
  • @uzumaxy:您需要有一个函数来执行此操作,但它不需要明确引用 self.name = '' 之类的属性。相反,您可能会遍历一个列表并使用类似于 setattr(self, 'name', '') 的内容。
  • 哦,很好的解决方案。谢谢你。
  • @uzumaxy:那是因为class_builder.instance_attributes['books'] 被设置为一个列表。 init_function 将为每个实例提供相同的列表。如果您希望每个实例都有一个不同的列表,您需要确保您的实现能够做到这一点。一种可能的解决方案:使instance_attributes 成为一个字典,从属性名称到返回属性值的空函数。然后,您可以为每个实例创建一个新列表。
  • @uzumaxy:就上下文而言,你现在所做的相当于:_name = ''; _books = []; class Human(object): def __init__(self): self.name = _name; self.books = _books
【解决方案2】:

他们都是班级级别的。 name 只是不可变的,所以乍一看它不像类级别的。大多数修改它的尝试都会创建一个具有相同名称的新实例级属性。

就像用普通的方式写一个类一样,你需要在构造函数中创建实例属性:

def __init__(self):
    self.name = ''
    self.books = []

def say_hello(self):
    # This prints a newline. The original didn't.
    print 'Hello!'

Human = type('Human', (object,), {
    '__init__': __init__,
    'say_hello': say_hello,
})

【讨论】:

    猜你喜欢
    • 2017-04-17
    • 1970-01-01
    • 2021-12-07
    • 2011-04-30
    • 2011-12-21
    • 2012-09-29
    • 2017-02-23
    • 1970-01-01
    • 2014-03-20
    相关资源
    最近更新 更多