简体   繁体   English

某些属性唯一的Python类实例

[英]Python class instances unique by some property

Suppose I want to create instances of my class freely, but if I instantiate with the same argument, I want to get the same unique instance representing that argument. 假设我想自由地创建我的类的实例,但是如果我使用相同的参数进行实例化,我想获得表示该参数的相同唯一实例。 For example: 例如:

a = MyClass('Instance 1');
b = MyClass('Instance 2');
c = MyClass('Instance 1');

I would want a == c to be True , based on the unique identifier I passed in. 根据我传入的唯一标识符,我希望a == cTrue

Note: 注意:

(1) I'm not talking about manipulating the equality operator-- I want a to really be the same instance as c . (1)我不是在谈论操纵相等运算符 - 我想要a真正与c相同的实例。

(2) This is intended as library code, so uniqueness has to be enforced -- we can't just count on users doing the right thing (whatever that is). (2)这是作为库代码,因此必须强制执行 - 我们不能只指望用户做正确的事情(无论是什么)。

Is there a canonical way of achieving this? 是否有规范的方法来实现这一目标? I run into this pattern all the time, but I usually see solutions involving shadow classes, meant for only internal instantiation. 我一直遇到这种模式,但我经常看到涉及阴影类的解决方案,仅用于内部实例化。 I think I have a cleaner solution, but it does involve a get() method, and I'm wondering if I can do better. 我想我有一个更清洁的解决方案,但它确实涉及一个get()方法,我想知道我是否可以做得更好。

I'd use a metaclass. 我用的是元类。 This solution avoids calling __init__() too many times: 此解决方案避免多次调用__init__()

class CachedInstance(type):
    _instances = {}
    def __call__(cls, *args):
        index = cls, args
        if index not in cls._instances:
            cls._instances[index] = super(CachedInstance, cls).__call__(*args)
        return cls._instances[index]

class MyClass(metaclass=CachedInstance):
    def __init__(self, name):
        self.name = name

a = MyClass('Instance 1');
b = MyClass('Instance 2');
c = MyClass('Instance 1');
assert a is c
assert a is not b

Reference and detailed explanation: https://stackoverflow.com/a/6798042/8747 参考和详细说明: https//stackoverflow.com/a/6798042/8747

This can be done (assuming that args are all hashable) 这可以做到(假设args都可以清洗)

 class MyClass:

     instances = {}

     def __new__(cls, *args):
          if args in cls.instances:
               return cls.instances[args]
          self = super().__new__(cls)
          cls.instances[args] = self
          return self

a = MyClass('hello')
b = MyClass('hello')
c = MyClass('world')

a is b and a == b and a is not c and a != c  # True

is is the python operator that shows two objects are the same instance. is是python运算符,它显示两个对象是同一个实例。 == falls back to is on objects where it is not overidden. ==回落到is在哪里它不overidden对象。


As pointed out in the comments, this can be a bit troubling if you have an __init__ with side effects. 正如评论中指出的那样,如果你有__init__有副作用,这可能会有点麻烦。 Here's an implementation that avoids that: 这是一个避免这种情况的实现:

class Coord:

     num_unique_instances = 0
     _instances = {}

     def __new__(cls, x, y):

         if (x, y) in cls._instances:
              return cls._instances[x, y]

         self = super().__new__(cls)

         # __init__ logic goes here  -- will only run once
         self.x = x
         self.y = y
         cls.num_unique_instances += 1
         # __init__ logic ends here

         cls._instances[x, y] = self
         return self

     # no __init__ method

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM