我编写了一个带有实例属性的类,称之为名称。确保类的所有实例都具有唯一名称的最佳方法是什么?我是否在类下创建一个集合,并且每次创建新实例时,名称都会添加到 init 定义中的集合中?由于集合是唯一元素的集合,因此我可以验证新实例的名称是否可以成功添加到集合中。
编辑:我希望能够提供名称而不是为其分配 UUID。所以 mementum 的方法似乎是最稳健的。jpkotta 的就是我会做的。
您可以使用元类(例如)控制实例创建并确保名称是唯一的。假设该方法采用一个没有默认值的__init__参数name
class MyClass(object):
def __init__(self, name, *args, **kwargs):
self.name = name
Run Code Online (Sandbox Code Playgroud)
显然,实例可以与 this 有相同的名称。让我们使用metaclass(使用兼容的Python 2/3语法)
class MyMeta(type):
_names = set()
@classmethod
def as_metaclass(meta, *bases):
'''Create a base class with "this metaclass" as metaclass
Meant to be used in the definition of classes for Py2/3 syntax equality
Args:
bases: a list of base classes to apply (object if none given)
'''
class metaclass(meta):
def __new__(cls, name, this_bases, d):
# subclass to ensure super works with our methods
return meta(name, bases, d)
return type.__new__(metaclass, str('tmpcls'), (), {})
def __call__(cls, name, *args, **kwargs):
if name in cls._names:
raise AttributeError('Duplicate Name')
cls._names.add(name)
return type.__call__(cls, name, *args, **kwargs)
class MyClass(MyMeta.as_metaclass()):
def __init__(self, name, *args, **kwargs):
self.name = name
a = MyClass('hello')
print('a.name:', a.name)
b = MyClass('goodbye')
print('b.name:', b.name)
try:
c = MyClass('hello')
except AttributeError:
print('Duplicate Name caught')
else:
print('c.name:', c.name)
Run Code Online (Sandbox Code Playgroud)
哪个输出:
class MyClass(object):
def __init__(self, name, *args, **kwargs):
self.name = name
Run Code Online (Sandbox Code Playgroud)
使用该metaclass技术,您甚至可以避免将其作为name参数,并且可以为每个实例自动生成名称。
import itertools
class MyMeta(type):
_counter = itertools.count()
@classmethod
def as_metaclass(meta, *bases):
'''Create a base class with "this metaclass" as metaclass
Meant to be used in the definition of classes for Py2/3 syntax equality
Args:
bases: a list of base classes to apply (object if none given)
'''
class metaclass(meta):
def __new__(cls, name, this_bases, d):
# subclass to ensure super works with our methods
return meta(name, bases, d)
return type.__new__(metaclass, str('tmpcls'), (), {})
def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs)
obj.name = '%s_%d' % (cls.__name__, next(cls._counter))
return obj
class MyClass(MyMeta.as_metaclass()):
pass
a = MyClass()
print('a.name:', a.name)
b = MyClass()
print('b.name:', b.name)
c = MyClass()
print('c.name:', c.name)
Run Code Online (Sandbox Code Playgroud)
输出:
class MyMeta(type):
_names = set()
@classmethod
def as_metaclass(meta, *bases):
'''Create a base class with "this metaclass" as metaclass
Meant to be used in the definition of classes for Py2/3 syntax equality
Args:
bases: a list of base classes to apply (object if none given)
'''
class metaclass(meta):
def __new__(cls, name, this_bases, d):
# subclass to ensure super works with our methods
return meta(name, bases, d)
return type.__new__(metaclass, str('tmpcls'), (), {})
def __call__(cls, name, *args, **kwargs):
if name in cls._names:
raise AttributeError('Duplicate Name')
cls._names.add(name)
return type.__call__(cls, name, *args, **kwargs)
class MyClass(MyMeta.as_metaclass()):
def __init__(self, name, *args, **kwargs):
self.name = name
a = MyClass('hello')
print('a.name:', a.name)
b = MyClass('goodbye')
print('b.name:', b.name)
try:
c = MyClass('hello')
except AttributeError:
print('Duplicate Name caught')
else:
print('c.name:', c.name)
Run Code Online (Sandbox Code Playgroud)
a.name = b.name要完成问题并回答有关防止(或已使用的任何其他名称)的评论,可以使用一种descriptor基于方法
class DescName(object):
def __init__(self):
self.cache = {None: self}
def __get__(self, obj, cls=None):
return self.cache[obj]
def __set__(self, obj, value):
cls = obj.__class__
if value in cls._names:
raise AttributeError('EXISTING NAME %s' % value)
try:
cls._names.remove(self.cache[obj])
except KeyError: # 1st time name is used
pass
cls._names.add(value)
self.cache[obj] = value
class MyClass(object):
_names = set()
name = DescName()
def __init__(self, name, *args, **kwargs):
self.name = name
a = MyClass('hello')
print('a.name:', a.name)
b = MyClass('goodbye')
print('b.name:', b.name)
try:
c = MyClass('hello')
except AttributeError:
print('Duplicate Name caught')
else:
print('c.name:', c.name)
a.name = 'see you again'
print('a.name:', a.name)
try:
a.name = b.name
except AttributeError:
print('CANNOT SET a.name to b.name')
else:
print('a.name %s = %s b.name' % (a.name, b.name))
Run Code Online (Sandbox Code Playgroud)
具有预期的输出(名称不能在__init__分配期间重复使用)
a.name: hello
b.name: goodbye
Duplicate Name caught
Run Code Online (Sandbox Code Playgroud)
编辑:
由于OP赞成这种方法,因此组合metaclass方法descriptor涵盖:
name在类创建期间descriptor添加的类属性metaclassname在实例到达之前每个实例初始化__init__name赋值操作也具有唯一性
在类内部存储set并itertools.counter控制名称唯一性descriptor,从而消除类本身的污染
import itertools
class MyMeta(type):
_counter = itertools.count()
@classmethod
def as_metaclass(meta, *bases):
'''Create a base class with "this metaclass" as metaclass
Meant to be used in the definition of classes for Py2/3 syntax equality
Args:
bases: a list of base classes to apply (object if none given)
'''
class metaclass(meta):
def __new__(cls, name, this_bases, d):
# subclass to ensure super works with our methods
return meta(name, bases, d)
return type.__new__(metaclass, str('tmpcls'), (), {})
def __call__(cls, *args, **kwargs):
obj = type.__call__(cls, *args, **kwargs)
obj.name = '%s_%d' % (cls.__name__, next(cls._counter))
return obj
class MyClass(MyMeta.as_metaclass()):
pass
a = MyClass()
print('a.name:', a.name)
b = MyClass()
print('b.name:', b.name)
c = MyClass()
print('c.name:', c.name)
Run Code Online (Sandbox Code Playgroud)
其输出预期:
a.name: MyClass_0
b.name: MyClass_1
c.name: MyClass_2
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3064 次 |
| 最近记录: |