下面的代码(编辑:实际上,事实证明它没有!),但我不喜欢在{{1}之后出现的挂起return True
语句阻止。
try: except:
是否有更多pythonic方法为此类编写class MySlottedClass(object):
def __new__(klass, **slots):
klass.__slots__ = []
for k in slots:
klass.__slots__.append(k)
return super(MySlottedClass,klass).__new__(klass)
def __init__(self, **slots):
for k,v in slots.items():
setattr(self,k,v)
super(MySlottedClass,self).__new__()
def __eq__(self, other):
for slot in self.__slots__:
try:
assert getattr(self, slot) == getattr(other,slot), "Not Equal"
except (AssertionError, AttributeError):
return False
return True
##Testing
##Note that the above class definition is just a skeleton
##The below objects are created using 4 different but identically defined classes
##In the actual problem, I am using a metaclass to make these classes dynamically
msc1 = MySlottedClassABC(a=1,b=1,c=3)
msc2 = MySlottedClassAB(a=1,b=1)
msc3 = MySlottedClassBA(b=2,a=1)
msc4 = MySlottedClassXY(x=1,y=2)
assert msc1!=msc2
assert msc2==msc3
assert msc3==msc2
assert msc2!=msc4
方法?
答案 0 :(得分:7)
return True
没问题。我认为更大的问题是使用assert
进行流量控制。如果用户在命令行上将-O
传递给python
,则断言根本不会运行。你应该写一些更像这样的东西:
for slot in self.__slots__:
if not hasattr(other, slot) or getattr(self, slot) != getattr(other,slot):
return False
return True
此外,__slots__
需要在课程级别定义才能工作,而不是在__init__
内:
class Foo(object):
__slots__ = ['a', 'b', 'c']
如果你的项目数量可变,你可能根本就不应该使用__slots__
。
答案 1 :(得分:3)
<罢工>呃,没关系,我明白了。这很明显:
def __eq__(self, other):
try:
for slot in self.__slots__:
assert getattr(self, slot) == getattr(other,slot), "Not Equal"
except (AssertionError, AttributeError):
return False
else:
return True
我应该关闭这个问题所以我看起来不太愚蠢。
编辑:不,不好!
感谢大家的帮助,我现在明白这种做法存在很多问题。首先,我不应该使用assert
,因为它主要用于测试,并且可以关闭。其次,代码没有给出MySlottedClass(a=1,b=2)==MySlottedClass(a=1,b=2,c=3)
的预期结果。
我想出了这样的方式。请注意,类定义重复4次,因此我可以测试下面不同类的对象的比较;但是,在创建实例之前,所有类都是相同的。另请注意,在实际用例中,我使用元类自动生成这些类(并且__eq__
被定义为该元类的一部分)。
class MySlottedClassAB(object):
def __new__(klass, **slots):
klass.__slots__ = []
for k in slots:
klass.__slots__.append(k)
return super(MySlottedClassAB,klass).__new__(klass)
def __init__(self, **slots):
for k,v in slots.items():
setattr(self,k,v)
super(MySlottedClassAB,self).__init__()
def __eq__(self, other):
if set(self.__slots__) != set(other.__slots__): return False
for slot in self.__slots__:
if getattr(self, slot) != getattr(other,slot):
return False
return True
def __ne__(self, other):
return not self == other
class MySlottedClassBA(object):
def __new__(klass, **slots):
klass.__slots__ = []
for k in slots:
klass.__slots__.append(k)
return super(MySlottedClassBA,klass).__new__(klass)
def __init__(self, **slots):
for k,v in slots.items():
setattr(self,k,v)
super(MySlottedClassBA,self).__init__()
def __eq__(self, other):
if set(self.__slots__) != set(other.__slots__): return False
for slot in self.__slots__:
if getattr(self, slot) != getattr(other,slot):
return False
return True
def __ne__(self, other):
return not self == other
class MySlottedClassXY(object):
def __new__(klass, **slots):
klass.__slots__ = []
for k in slots:
klass.__slots__.append(k)
return super(MySlottedClassXY,klass).__new__(klass)
def __init__(self, **slots):
for k,v in slots.items():
setattr(self,k,v)
super(MySlottedClassXY,self).__init__()
def __eq__(self, other):
if set(self.__slots__) != set(other.__slots__): return False
for slot in self.__slots__:
if getattr(self, slot) != getattr(other,slot):
return False
return True
def __ne__(self, other):
return not self == other
class MySlottedClassABC(object):
def __new__(klass, **slots):
klass.__slots__ = []
for k in slots:
klass.__slots__.append(k)
return super(MySlottedClassABC,klass).__new__(klass)
def __init__(self, **slots):
for k,v in slots.items():
setattr(self,k,v)
super(MySlottedClassABC,self).__init__()
def __eq__(self, other):
if set(self.__slots__) != set(other.__slots__): return False
for slot in self.__slots__:
if getattr(self, slot) != getattr(other,slot):
return False
return True
def __ne__(self, other):
return not self == other
以下是测试程序:
##Testing
msc1 = MySlottedClassABC(a=1, b=2, c=3)
msc2 = MySlottedClassAB(a=1, b=2)
msc3 = MySlottedClassBA(b=2, a=1)
msc4 = MySlottedClassXY(x=1, y=2)
assert msc1 != msc2
assert msc2 != msc1
assert msc2 == msc3
assert msc3 == msc2
assert msc3 != msc4
assert msc4 != msc3
然而,在测试Joran Beasley's answer之后,我发现令我惊讶的是它产生了上面的IDENTICAL结果,代码更短更敏感。因此,实现此目的的最佳方法似乎是简单地比较两个__dict__
属性。
答案 2 :(得分:2)
好像你正在尝试重新创建namedtuple
。使用namedtuple
将允许动态创建类,测试相等性和其他有趣的东西。缺点是,由于元组是不可变的,因此要命名为元组,您必须创建一个新对象而不是更新属性。 namedtuples不会检查您的广告位的顺序,因此您必须按字典顺序订购广告位,或添加自己的__eq__
方法来说明广告位顺序。
使用示例:
from collections import namedtuple
MySlottedClassAB = namedtuple("MySlottedClassAB", ['a', 'b'])
MySlottedClassABC = namedtuple("MySlottedClassABC", ['a', 'b', 'c'])
class MySlottedClassBA(namedtuple("MySlottedClassBA", ['b', 'a'])):
def addAB(self):
return self.a + self.b
msc1 = MySlottedClassAB(a=1, b=2)
msc2 = MySlottedClassBA(b=2, a=1)
msc3 = MySlottedClassABC(1, 2, 3)
print(msc1)
print(msc2)
print(msc3)
print("{} == {} is {}".format(msc1, msc1, msc1==msc1))
print("{} == {} is {}".format(msc1, msc2, msc1==msc2))
print("{} == {} is {}".format(msc1, msc3, msc1==msc3))
print("msc2.addAB() is {}".format(msc2.addAB()))
如果你的插槽和可变性的顺序很重要,那么以下内容将起作用(对于python 2)。
class MySlottedClassMeta(type):
def __init__(cls, name, bases, attrs):
super(MySlottedClassMeta, cls).__init__(name, bases, attrs)
def __new__(metacls, name, bases, attrs):
assert "__slots__" in attrs
attrs["_ordered_slots"] = tuple(sorted(attrs["__slots__"]))
attrs["__init__"] = create_init(attrs["__slots__"])
attrs["__eq__"] = create_eq()
attrs["__str__"] = create_str()
cls = super(MySlottedClassMeta, metacls).__new__(metacls, name, bases, attrs)
return cls
def create_init(slots):
args = ", ".join(slots)
assignments = "\n ".join("self.{0} = {0}".format(attr) for attr in slots)
init_source = """
def __init__(self, {}):
{}
""".format(args, assignments)
exec(init_source, globals(), None)
return __init__
def create_eq():
def __eq__(self, other):
try:
same_slots = self._ordered_slots == other._ordered_slots
except AttributeError:
return False
if not same_slots:
return False
return all(getattr(self, attr) == getattr(other, attr)
for attr in self._ordered_slots)
return __eq__
def create_str():
def __str__(self):
attr_values = ", ".join("{}={}".format(s, getattr(self, s)) for s in self.__slots__)
return "{}({})".format(self.__class__.__name__, attr_values)
return __str__
class MySlottedClassXY(object):
__slots__ = ['x', 'y']
__metaclass__ = MySlottedClassMeta
class MySlottedClassYX(object):
__slots__ = ['y', 'x']
__metaclass__ = MySlottedClassMeta
xy1 = MySlottedClassXY(x=1,y=2)
xy2 = MySlottedClassXY(1, 2)
yx = MySlottedClassYX(x=1, y=2)
print(xy1.__slots__)
print(yx.__slots__)
assert xy1 == xy1
assert xy1 == xy2
assert xy1 == yx
有人指出__slots__
在几乎所有情况下都是过度杀伤力。 Guido Van Rossum表示,基于对新风格类中属性查找性能的毫无根据的担忧,它们是过早的优化。 Guido还指出,当您需要创建批次小对象时,__slots__
可以减少程序的内存占用量。
我担心[新]级系统的所有变化都会对性能产生负面影响。 ...因此,
__slots__
的使用是优化数据属性查找的一种方法 - 如果你愿意,可以使用后备,以防人们对新类系统的性能影响感到失望。事实证明这是不必要的,但到那时删除__slots__
当然为时已晚。
http://python-history.blogspot.co.uk/2010/06/inside-story-on-new-style-classes.html
答案 3 :(得分:1)
def __eq__(self,other):
return self.__dict__== other.__dict__
应该有效