我知道这个已经被覆盖了,也许不是构造类的最python的方式,但是我有很多不同的maya节点类,有很多用于检索/设置节点数据的@属性,我想看看是否程序性地构建属性减少了开销/维护。
我需要重新实现__setattr__以保持标准行为,但对于某些特殊属性,值是get/set为外部对象。
我已经看到了在堆栈溢出上重新实现__setattr__的例子,但我似乎错过了一些东西。
我不认为我在维护setAttr
的默认功能下面是一个例子:
externalData = {'translateX':1.0,'translateY':1.0,'translateZ':1.0}
attrKeys = ['translateX','translateY','translateZ']
class Transform(object):
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
raise AttributeError("No attribute named [%s]" %name)
def __setattr__(self, name, value):
print 'Setting --->', name
super(Transform, self).__setattr__(name, value)
if name in attrKeys:
externalData[name] = value
myInstance = Transform()
myInstance.translateX
# Result: 1.0 #
myInstance.translateX = 9999
myInstance.translateX
# Result: 9999 #
myInstance.name = 'myName'
myInstance.name
# AttributeError: No attribute named [name] #
!
这对我有用:
class Transform(object):
def __getattribute__(self, name):
if name in attrKeys:
return externalData[name]
return super(Transform, self).__getattribute__(name)
def __setattr__(self, name, value):
if name in attrKeys:
externalData[name] = value
else:
super(Transform, self).__setattr__(name, value)
然而,我不确定这是一个好的路线。
如果外部操作是耗时的(例如,您使用它来掩饰对数据库或配置文件的访问),您可能会给代码的用户关于成本的错误印象。在这种情况下,你应该使用一个方法,让用户明白他们正在启动一个操作,而不仅仅是查看数据。
OTOH如果访问是快速的,注意不要破坏类的封装。如果你这样做是为了获得玛雅场景数据(pymel风格,或者在这个例子中),这不是一个大问题,因为时间成本和数据的稳定性或多或少得到保证。然而,你想要避免的场景在你发布的示例代码:这将是很容易假设已经设置了"translateX"给定的值,它会留在那里,事实上有很多方法,外部变量的内容可以弄乱,阻止你能够知道你的不变量,而使用类。如果该类是一次性使用的(例如,它的语法适用于在as循环中进行大量快速重复处理,而没有其他操作正在运行),则可以使用它-但如果不是,则将数据内部化到实例中。
最后一个问题:如果你有"很多类",你也将不得不做很多样板文件来完成这个工作。如果你试图包装玛雅场景数据,阅读描述符(这里有一个伟大的5分钟视频)。您可以包装典型的转换属性,例如,像这样:
import maya.cmds as cmds
class MayaProperty(object):
'''
in a real implmentation you'd want to support different value types,
etc by storing flags appropriate to different commands....
'''
def __init__(self, cmd, flag):
self.Command = cmd
self.Flag = flag
def __get__(self, obj, objtype):
return self.Command(obj, **{'q':True, self.Flag:True} )
def __set__(self, obj, value):
self.Command(obj, **{ self.Flag:value})
class XformWrapper(object):
def __init__(self, obj):
self.Object = obj
def __repr__(self):
return self.Object # so that the command will work on the string name of the object
translation = MayaProperty(cmds.xform, 'translation')
rotation = MayaProperty(cmds.xform, 'rotation')
scale = MayaProperty(cmds.xform, 'scale')
在实际代码中,你需要错误处理和更清晰的配置,但你明白这个想法。
上面链接的例子讨论了当你有很多属性描述符要配置时使用元类来填充类,如果你不想担心所有的样板文件,这是一个很好的方法(尽管它确实有一个小的启动时间惩罚-我认为这是臭名昭著的Pymel启动爬行的原因之一…)
我决定使用@theodox s并使用描述符这看起来工作得很好:
class Transform(object):
def __init__(self, name):
self.name = name
for key in ['translateX','translateY','translateZ']:
buildNodeAttr(self.__class__, '%s.%s' % (self.name, key))
def buildNodeAttr(cls, plug):
setattr(cls, plug.split('.')[-1], AttrDescriptor(plug))
class AttrDescriptor(object):
def __init__(self, plug):
self.plug = plug
def __get__(self, obj, objtype):
return mc.getAttr(self.plug)
def __set__(self, obj, val):
mc.setAttr(self.plug, val)
myTransform = Transform(mc.createNode('transform', name = 'transformA'))
myTransform.translateX = 999
作为旁注…
原来我的原始代码只需要将getattribute与getattr
no super needed
为什么不在__getattribute__
中做同样的事情呢?
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
# raise AttributeError("No attribute named [%s]" %name)
return super(Transform, self).__getattribute__(name)
测试代码
myInstance = Transform()
myInstance.translateX
print(externalData['translateX'])
myInstance.translateX = 9999
myInstance.translateX
print(externalData['translateX'])
myInstance.name = 'myName'
print myInstance.name
print myInstance.__dict__['name']
输出:Getting ---> translateX
1.0
Setting ---> translateX
Getting ---> translateX
9999
Setting ---> name
Getting ---> name
myName
Getting ---> __dict__
myName
在您的代码片段中:
class Transform(object):
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
raise AttributeError("No attribute named [%s]" %name)
def __setattr__(self, name, value):
print 'Setting --->', name
super(Transform, self).__setattr__(name, value)
if name in attrKeys:
externalData[name] = value
看,在你的__setattr__()
中,当你调用myInstance.name = 'myName'
时,name
不在attrKeys
中,所以它不会插入到externalData字典中,而是添加到self.__dict__['name'] = value
因此,当您尝试查找特定名称时,您没有进入externalData
字典,因此__getattribute__
将异常引发。
你可以通过改变__getattribute__
来解决这个问题,而不是引发一个异常,如下所示:
def __getattribute__(self, name):
print 'Getting --->', name
if name in attrKeys:
return externalData[name]
else:
return object.__getattribute__(self, name)