使用对象组合时如何每次实例化唯一对象?

2 投票
6 回答
1703 浏览
提问于 2025-04-17 04:43

举个例子,这里有几个简单的对象会一起使用。顺便说一下,这段代码是用 Python 2.7.2 写的。

class Student(object):
    def __init__(self, tool):
        self.tool = tool

    def draw(self):
        if self.tool.broken != True:
            print "I used my tool. Sweet."
        else:
            print "My tool is broken. Wah."

class Tool(object):
    def __init__(self, name):
        self.name = name
        self.broken = False

    def break(self):
        print "The %s busted." % self.name
        self.broken = True

Hammer = Tool(hammer)
Billy = Student(Hammer)
Tommy = Student(Hammer)

代码差不多就是这些,你能看出我想表达的意思。如果我调用 Hammer.break(),那就是在同一个对象上调用;如果比利的锤子坏了,汤米的锤子也坏了(毕竟它们其实是同一个锤子)。

显然,如果程序只限于比利和汤米这两个学生实例,那解决办法就很简单——再创建更多的锤子。但是我问这个问题显然是因为事情并没有那么简单,嘿。我想知道是否可以创建一些对象,每次被调用时都能显示为独特的实例。

补充:我得到的回答让我觉得我对实例化的理解有很大的漏洞。如果我有这样的代码:

class Foo(object):
    pass

class Moo(Foo):
    pass

class Guy(object):
    def __init__(self, thing):
        self.thing = thing

Bill = Guy(Moo())
Steve = Guy(Moo())

每次我使用 Moo(),这算是一个独立的实例吗,还是它们都指向同一个对象?如果它们是独立的,那我的问题就可以撤回,因为这会让我大吃一惊。

6 个回答

2

我会尽量简短。嗯……我总是想简短,但我成功的程度基本上是随机的,可能是0到永远。所以就这样吧。

哈哈,你连宣布要简短这件事都没做到简短。

首先,我们需要搞清楚“被调用出来”是什么意思。假设你希望每次执行 self.tool = object 时都能得到一个新的锤子。你并不想每次访问工具属性时都得到一个新的实例,比如说,每次你检查 self.tool.broken 时都得到一个新的、假设是完好的锤子。

这里有几种方法。

第一种,给工具类(Tool)添加一个复制方法,这个方法会生成一个新的对象,这个新对象应该和原来的对象相等,但它是一个不同的实例。例如:

class Tool:

    def __init__(self, kind):
        self.kind = kind
        self.broken = False

    def copy(self):
        result = Tool(self.kind)
        result.broken = self.broken
        return result

然后在学生类(Student)的初始化方法里,你可以这样写:

    self.tool = tool.copy()

第二种选择,使用工厂函数。

def makehammer():
    return Tool(hammer)

class Student:
    def __init__(self, factory):
        self.tool = factory()

Billy = Student(makehammer)

我想不出有什么办法可以在Python中写出 self.tool = object 这一行,并让对象自动生成一个副本,我也觉得你不想这样。Python的一个优点就是所见即所得(WYSIWYG)。如果你想要魔法效果,那就用C++吧。我觉得当你不仅看不出一行代码在做什么,甚至连它是否在做一些“特别的事情”都看不出来时,代码就变得很难理解。

注意,你还可以用工厂“对象”做得更花哨。例如:

class RealisticFactory:
    def __init__(self, kind, failurerate):
        self.kind = kind
        self.failurerate = failurerate

    def make(self):
        result = Tool(self.kind)
        if random.random() < self.failurerate:
            result.broken = True
        if (self.failurerate < 0.01):
            self.failurerate += 0.0001
        return result

factory = RealisticFactory(hammer, 0.0007)
Billy = Student(factory.make)
Tommy = Student(factory.make) # Tommy's tool is slightly more likely to be broken
3

你需要为每个学生创建一个新的工具实例。

class Student(object):
    def __init__(self, tool):
        self.tool = tool

    def draw(self):
        if self.tool.broken != True:
            print "I used my tool. Sweet."
        else:
            print "My tool is broken. Wah."

class Tool(object):
    def __init__(self, name):
        self.name = name
        self.broken = False

    def break(self):
        print "The %s busted." % self.name
        self.broken = True

# Instead of instance, make it a callable that returns a new one
def Hammer():
    return Tool('hammer')

# Pass a new object, instead of the type
Billy = Student(Hammer())
Tommy = Student(Hammer())
0

看到这里的回答和想起Python的哲学,我决定自己回答自己的问题,想说“我可能应该再想想。”

我把自己的问题重新表述一下作为答案。假设我有这样一个小程序:

class Item(object):
    def __init__(self):
        self.broken = False

    def smash(self):
        print "This object broke."
        self.broken = True

class Person(object):
    def __init__(self, holding):
        self.holding = holding

    def using(self):
        if self.holding.broken != True:
            print "Pass."
        else:
            print "Fail."

Foo = Person(Item())
Bar = Person(Item())

Foo.holding.smash()
Foo.using()
Bar.using()

这个程序对Foo.using()会返回“Fail”,对Bar.using()会返回“Pass”。在认真思考我在做什么之后,我发现“Foo.holding = Item()”和“Bar.holding = Item()”明显是不同的实例。我甚至运行了这个简单的程序来证明我的想法是对的,结果果然没让你们这些专业人士失望,它确实是这样。所以我撤回我的问题,因为我在问这个问题的时候并没有认真思考。有趣的是,在我正在做的程序中,我其实已经是这样做的,但我一直以为这是错误的做法。所以谢谢你们的耐心。

撰写回答