代码之家  ›  专栏  ›  技术社区  ›  John Pirie

python类实例的某些属性是唯一的

  •  1
  • John Pirie  · 技术社区  · 6 年前

    假设我想自由地创建类的实例,但是如果我用相同的参数实例化,我想得到表示该参数的相同的唯一实例。例如:

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

    我想要 a == c 成为 True ,基于我传入的唯一标识符。

    注:

    (1)我不是在说操纵等式运算符——我想 a 真正成为 c .

    (2)这是库代码,因此唯一性必须是 强制执行的 --我们不能指望用户做正确的事情(不管是什么)。

    有没有一种规范的方法来实现这一点?我经常遇到这种模式,但我通常看到的解决方案涉及影子类,只用于内部实例化。我想我有一个更简洁的解决方案,但它确实涉及get()方法,我想知道我是否可以做得更好。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Robᵩ    6 年前

    我会用一个元类。此解决方案避免调用 __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
    

    参考及详细说明: https://stackoverflow.com/a/6798042/8747

        2
  •  1
  •   FHTMitchell    6 年前

    这是可以做到的(假设 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 是显示两个对象是同一实例的python运算符。 == 回到 在没有被遮住的物体上。


    正如评论中指出的,如果您有 __init__ 有副作用。下面是一个避免这种情况的实现:

    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