在Python中,有一些具有高度约束实例的内置类的示例。例如,“None”是其类的唯一实例,并且在bool类中只有两个对象,“True”和“False”(我希望到目前为止我或多或少都是正确的)。
另一个很好的例子是整数:如果a和b是int类型的实例,那么a == b意味着a是b。
两个问题:
如何创建具有类似约束实例的类?例如,我们可以要求一个只有5个实例的类。或者可能存在无限多个实例,例如int类型,但这些实例不是任意的。
如果整数形成一个类,为什么int()给出0实例?将此与用户定义的类Cl进行比较,其中Cl()将给出类的实例,而不是特定的唯一实例,如0.不应该int()返回未指定的整数对象,即一个没有指定值的整数?
答案 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)
__new__
返回其中一个而不是创建一个新对象,或缓存创建的实例(weakrefs在这里很方便)并返回其中一个而不是创建一个新对象 单例如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)
我认为您正在考虑的概念的一些名称是interning和immutable 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