如何使用高度约束的实例在Python中创建类

时间:2009-07-19 04:42:11

标签: python math class

在Python中,有一些具有高度约束实例的内置类的示例。例如,“None”是其类的唯一实例,并且在bool类中只有两个对象,“True”和“False”(我希望到目前为止我或多或少都是正确的)。

另一个很好的例子是整数:如果a和b是int类型的实例,那么a == b意味着a是b。

两个问题:

  1. 如何创建具有类似约束实例的类?例如,我们可以要求一个只有5个实例的类。或者可能存在无限多个实例,例如int类型,但这些实例不是任意的。

  2. 如果整数形成一个类,为什么int()给出0实例?将此与用户定义的类Cl进行比较,其中Cl()将给出类的实例,而不是特定的唯一实例,如0.不应该int()返回未指定的整数对象,即一个没有指定值的整数?

7 个答案:

答案 0 :(得分:5)

你所说的是给出一个类值语义,这通常是通过以正常方式创建类实例来完成的,但要记住每个实例,如果要创建匹配的实例,请给出已经创建了实例。在python中,这可以通过重载类__new__ method来实现。

简单的例子,假设我们想使用整数对来表示坐标,并且具有正确的值语义。

class point(object):
    memo = {}
    def __new__(cls, x, y):
        if (x, y) in cls.memo:         # if it already exists, 
            return cls.memo[(x, y)]    # return the existing instance
        else:                          # otherwise, 
            newPoint = object.__new__(cls) # create it, 
            newPoint.x = x             # initialize it, as you would in __init__
            newPoint.y = y             
            cls.memo[(x, y)] = newPoint # memoize it, 
            return newPoint            # and return it!

答案 1 :(得分:4)

看起来#1已经得到了很好的回答,我只是想解释一个与#2相关的原则,似乎所有受访者都错过了这个原则:对于大多数内置类型,调用没有参数的类型( “default constructor”)返回该类型的实例,其值为false 。这意味着容器类型的空容器,数字类型的数字等于零。

>>> import decimal
>>> decimal.Decimal()
Decimal("0")
>>> set()
set([])
>>> float()
0.0
>>> tuple()
()
>>> dict()
{}
>>> list()
[]
>>> str()
''
>>> bool()
False

请参阅?非常正规!而且,对于可变类型,像大多数容器一样,调用类型总是返回一个新实例;对于不可变类型,如数字和字符串,它无关紧要(这是一种可能的内部优化,可以返回对现有不可变实例的新引用,但实现不需要执行这样的优化,如果是这样,它可以并且经常会有选择地执行它们)因为将不可变类型实例与is或等价地id()进行比较永远不正确。

如果您设计的某些实例可以将其评估为false(通过在类中使用__len____nonzero__个特殊方法),则建议遵循相同的规则({{1} } [或__init__ for immutables],如果没有参数调用[[{1}} __new__以及self当然为'{1}},请准备[[new ,if mutable]]类的“空”或“零样”实例。

答案 2 :(得分:3)

对于第一个问题,您可以实现单例类设计模式http://en.wikipedia.org/wiki/Singleton_pattern,您应该从中限制实例数。

对于第二个问题,我认为这样可以解释您的问题http://docs.python.org/library/stdtypes.html

因为整数是类型,所以它有局限性。

这是另一个资源...... http://docs.python.org/library/functions.html#built-in-funcs

答案 3 :(得分:2)

  1. 事先创建它们并从__new__返回其中一个而不是创建一个新对象,或缓存创建的实例(weakrefs在这里很方便)并返回其中一个而不是创建一个新对象
  2. 整数很特别。实际上,这意味着您永远不能使用标识来比较它们,因为您将使用标识来比较其他对象。由于它们是不可变的并且很少以任何方式使用而不是值上下文,因此这不是一个问题。据我所知,这完成了实现的原因。 (而且由于没有明确表明这是一种不正确的方式,这是一个很好的决定。)
  3. 单例如None:使用要为变量赋予的名称创建类,然后将变量重新绑定到(唯一)实例,或者之后删除该类。当您想模拟getattr这样的界面时,这很方便,其中参数是可选的,但使用None与不提供值不同。

    class raise_error(object): pass
    raise_error = raise_error()
    
    def example(d, key, default=raise_error):
      """Return d[key] if key in d, else return default or
      raise KeyError if default isn't supplied."""
      try:
        return d[key]
      except KeyError:
        if default is raise_error:
          raise
        return default
    

答案 4 :(得分:2)

要回答如何创建约束实例的更一般的问题,它取决于约束。上面的两个示例都是一种“单例”,尽管第二个示例是一个变体,您可以在其中拥有一个类的多个实例,但每个输入值只有一个。

这些都可以通过覆盖类“__new__方法来完成,以便类创建实例(如果尚未创建),并且都返回它,存储它作为班级的一个属性(如上所述)。然而,稍微不那么强硬的方法是使用元类。这些是改变类行为的类,而单例是何时使用元类的一个很好的例子。最重要的是你可以重用元类。通过创建Singleton元类,您可以将此元类用于您拥有的所有Singletons。

维基百科上的一个很好的Python示例:http://en.wikipedia.org/wiki/Singleton_pattern#Python

以下是根据参数创建不同实例的变体: (它并不完美。如果传入一个dict参数,它会失败,例如。但它是一个开始):

# Notice how you subclass not from object, but from type. You are in other words
# creating a new type of type.
class SingletonPerValue(type):
    def __init__(cls, name, bases, dict):
        super(SingletonPerValue, cls).__init__(name, bases, dict)
        # Here we store the created instances later.
        cls.instances = {}

    def __call__(cls, *args, **kw):
        # We make a tuple out of all parameters. This is so we can use it as a key
        # This will fail if you send in unhasheable parameters.
        params = args + tuple(kw.items())
        # Check in cls.instances if this combination of parameter has been used:
        if params not in cls.instances:
            # No, this is a new combination of parameters. Create a new instance,
            # and store it in the dictionary:
            cls.instances[params] = super(SingletonPerValue, cls).__call__(*args, **kw)

        return cls.instances[params]


class MyClass(object):
    # Say that this class should use a specific metaclass:
    __metaclass__ = SingletonPerValue

    def __init__(self, value):
        self.value = value

print 1, MyClass(1)
print 2, MyClass(2)
print 2, MyClass(2)
print 2, MyClass(2)
print 3, MyClass(3)

但是Python中还有其他限制而不是实例化。其中许多可以使用元类完成。其他人有快捷方式,这是一个只允许你设置属性'items'和'fruit'的类。

class Constrained(object):
    __slots__ = ['items', 'fruit']

con = Constrained()
con.items = 6
con.fruit = "Banana"
con.yummy = True

如果你想要限制属性,但不是那么强大,你可以覆盖__getattr__, __setattr__ and __delattr__以使许多奇妙而可怕的事情发生。 :-)还有一些包可以让你设置属性约束等。

答案 5 :(得分:1)

我认为您正在考虑的概念的一些名称是interningimmutable objects

至于你的具体问题的答案,我认为对于#1,你可以在类方法中查找受约束的实例并返回它。

对于问题#2,我认为这只是你如何指定你的课程的问题。 int类的非特定实例将毫无用处,因此只需对其进行规范即可创建。

答案 6 :(得分:1)

注意:这不是你问题的真正答案,但更多的评论我不能适应“评论”空间。

请注意,a == b NOT 表示a is b

只有少数几个整数(如前一百个左右 - 我不确切知道)才真实,它只是CPython的一个实现细节,实际上是通过切换到Python 3.0而改变的。

例如:

>>> n1 = 4000
>>> n2 = 4000
>>> n1 == n2
True
>>> n1 is n2
False