动态创建一个类型

时间:2014-01-19 01:13:48

标签: python metaprogramming

我可以动态创建类定义,例如:

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”是类级别的? 如何使用实例级属性动态创建类型的定义?求助。

2 个答案:

答案 0 :(得分:3)

实际上,namebooks都属于班级。只是字符串是不可变的,所以当你使用uzumaxy.name = "Maxim"时,你会添加一个名为name的新属性来隐藏类name,而对于uzumaxy.books.append("Programming via .NET"),你就是访问现有(类)books并进行修改。您的代码等同于:

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!") }

答案 1 :(得分: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,
})