gpt4 book ai didi

python - 确保python中实例属性的唯一性

转载 作者:行者123 更新时间:2023-12-01 04:11:51 25 4
gpt4 key购买 nike

我编写了一个带有实例属性的类,称之为名称。确保类的所有实例都具有唯一名称的最佳方法是什么?我是否在类下创建一个集合,并且每次创建新实例时,名称都会添加到 init 定义中的集合中?由于集合是唯一元素的集合,因此我可以验证新实例的名称是否可以成功添加到集合中。

编辑:我希望能够提供名称而不是为其分配 UUID。所以 mementum 的方法似乎是最稳健的。 jpkotta 的就是我会做的。

最佳答案

您可以使用元类(例如)控制实例创建并确保名称是唯一的。假设 __init__ 方法采用一个没有默认值的参数 name

class MyClass(object):

def __init__(self, name, *args, **kwargs):
self.name = name

显然,实例可以与此具有相同的名称。让我们使用元类(使用兼容的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)

哪些输出:

a.name: hello
b.name: goodbye
Duplicate Name caught

使用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)

输出:

a.name: MyClass_0
b.name: MyClass_1
c.name: MyClass_2

要完成问题并回答有关防止 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))

具有预期的输出(在 __init__ 或赋值期间不能重用名称)

a.name: hello
b.name: goodbye
Duplicate Name caught
a.name: see you again
CANNOT SET a.name to b.name

编辑:

由于 OP 赞成这种方法,因此结合了元类描述符方法,其中涵盖:

  • name 类属性作为descriptor在类创建期间由metaclass 添加
  • name 每个实例在实例到达 __init__ 之前进行初始化
  • name 赋值操作也具有唯一性

  • descriptor类中存储控制名称唯一性的setitertools.counter,从而消除类本身的污染

import itertools


class MyMeta(type):
class DescName(object):
def __init__(self, cls):
self.cache = {None: self, cls: set()}
self.counter = {cls: itertools.count()}

def __get__(self, obj, cls=None):
return self.cache[obj]

def __set__(self, obj, value):
self.setname(obj, value)

def setname(self, obj, name=None):
cls = obj.__class__
name = name or '%s_%d' % (cls.__name__, next(self.counter[cls]))

s = self.cache[cls]
if name in s:
raise AttributeError('EXISTING NAME %s' % name)

s.discard(self.cache.get(obj, None))
s.add(name)
self.cache[obj] = name

def __new__(meta, name, bases, dct):
cls = super(MyMeta, meta).__new__(meta, name, bases, dct)
cls.name = meta.DescName(cls) # add the name class attribute
return cls

@classmethod
def as_metaclass(meta, *bases):
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):
# Instead of relying on type we do the new and init calls
obj = cls.__new__(cls, *args, **kwargs)
cls.name.setname(obj)
obj.__init__(*args, **kwargs)
return obj


class MyClass(MyMeta.as_metaclass()):
def __init__(self, *args, **kwargs):
print('__init__ with name:', self.name)


a = MyClass()
b = MyClass()
c = MyClass()

a.name = 'my new name'
print('a.name:', a.name)

try:
a.name = b.name
except AttributeError as e:
print(e)
else:
print('a.name %s == %s b.name' % (a.name, b.name))

输出预期:

__init__ with name: MyClass_0
__init__ with name: MyClass_1
__init__ with name: MyClass_2
a.name: my new name
EXISTING NAME MyClass_1

关于python - 确保python中实例属性的唯一性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34818622/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com