Python - 用super重新实现__setattr__

时间:2014-03-10 02:54:36

标签: python attributes transform maya procedural

我知道这个已被覆盖过,也许不是构建类的最pythonic方式,但我有很多不同的maya节点类,有很多@properties用于检索/设置节点数据,我想要看看程序性地构建属性是否会减少开销/豁免。

我需要重新实现__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] #

<!/ P>

4 个答案:

答案 0 :(得分:2)

这对我有用:

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如果访问速度很快,请注意不要破坏类的封装。如果你这样做是为了获得maya场景数据(pymel风格,或者在this example中),这不是什么大问题,因为时间成本和数据的稳定性或多或少得到保证。但是你想要避免你发布的示例代码中的场景:很容易假设已经将'translateX'设置为一个给定值,它将保持不变,其中实际上有很多方法可以实现外部变量可能会混乱,使您无法在使用类时知道不变量。如果该类是用于一次性使用(例如,它的语法糖用于在没有其他操作正在运行的循环中进行大量快速重复处理),您可以使用它 - 但如果没有,则将数据内部化到您的实例。 / p>

最后一个问题:如果你有“很多课程”,你还需要做很多样板才能完成这项工作。如果您要尝试包装Maya场景数据,请阅读描述符(here's a great 5-minute video)。您可以包装典型的转换属性,例如:

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创业公司爬行的原因之一......)

答案 1 :(得分:2)

我已决定使用@theodox并使用描述符 这看起来效果很好:

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

作为旁注......
事实证明,我的原始代码可以通过使用 getattr

切换 getattribute 来实现

没有超级需要

答案 2 :(得分:0)

为什么不在__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

答案 3 :(得分:0)

在您的代码段中:

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中{1}}

因此,当您尝试查找该特定名称时,您没有使用externalData词典,因此__getattribute__会引发异常。

您可以通过更改__getattribute__而不是如下所示引发异常更改来解决此问题:

def __getattribute__(self, name):
    print 'Getting --->', name
    if name in attrKeys:
        return externalData[name]
    else:
        return object.__getattribute__(self, name)