Как я должен создавать свойства, используя замыкание в Python? - PullRequest
0 голосов
/ 06 июня 2018

Я делаю некоторое кодирование для Maya с PyMel, и я пытаюсь создать некоторые свойства в своем классе Rig, чтобы обернуть некоторый код PyMel.Код для всех свойств был довольно похож, поэтому я решил, что это было бы хорошим местом для использования замыкания.

import pymel.core as pm
import riggermortis.utils as utils

class RigModule(object):
    def __init__:
        # blah blah irrelevant code here
        pass

    def createRootProperty(attrName):
        def getter(self):
            return pm.getAttr(self.root+'.'+attrName)
        def setter(self, nodeToLink):
            if self.root.hasAttr(attrName):
                pm.setAttr(self.root+'.'+attrName, nodeToLink)
            else:
                utils.linkNodes(self.root, nodeToLink, attrName)
        return property(fget=getter,fset=setter)

    hookConstraint = createRootProperty('hookConstraint')
    unhookTarget = createRootProperty('unhookTarget')
    moduleGrp = createRootProperty('moduleGrp')
    hookGrp = createRootProperty('hookGrp')

Функционально это работает, но Eclipse / PyDev говорит мне, что моей функции 'createRootProperty' необходимо в качестве первого аргумента 'self', поэтому мне интересно, что я делаю неправильно.

1 Ответ

0 голосов
/ 07 июня 2018

Для того, что вы делаете, затвор не нужен, кроме чистоты.Линтер считает, что это неправильно отформатированная функция-член, даже если он делает то, что вы хотите.

Вы можете просто переместить функцию области видимости класса, и линтер перестанет жаловаться - вы можете переименовать функцию с подчеркиваниемтак что никто случайно не думает, что это инструмент, а не часть инфраструктуры.

Если вы планируете делать это много, вы можете автоматизировать его в метакласс, который считывает список имен из поля класса и создает свойствапо мере необходимости.Существует более подробный пример этой стратегии здесь , но по сути метакласс получит копию словаря классов, когда класс определен, и у него есть возможность связываться с определением до его компиляции.Вы можете легко создать свойство на этом шаге:

def createRootProperty(name):
    # this is a dummy, but as long as
    # the return from this function
    # is a property descriptor you're good
    @property
    def me(self):
        return name, self.__class__

    return me


class PropertyMeta(type):

    # this gets called when a class using this meta is
    # first compiled.  It gives you a chance to intervene
    # in the class creation project

    def __new__(cls, name, bases, properties):
        # if the class has a 'PROPS' member, it's a list 
        # of properties to add
        roots = properties.get('PROPS', [])
        for r in roots:
            properties[r] = createRootProperty(r)
            print ("# added property '{}' to {}".format(r, name))

        return type.__new__( cls, name, bases, properties)


class RigModule(object):
    __metaclass__ = PropertyMeta
    PROPS = ['arm', 'head', 'leg']

    def __init__(self):
        pass

test = RigModule()
print test.arm

class Submodule(RigModule):
    # metaclass added properties are inheritable
    pass


test2 = Submodule()
print test2.leg

class NewProperties(RigModule):
    # they can also be augmented in derived classes
    PROPS = ['nose', 'mouth']

print NewProperties().nose
print NewProperties().arm


# added property 'arm' to RigModule
# added property 'head' to RigModule
# added property 'leg' to RigModule
#  ('arm', <class '__main__.RigModule'>)
#  ('leg', <class '__main__.Submodule'>)
# added property 'nose' to NewProperties
# added property 'mouth' to NewProperties
# ('nose', <class '__main__.NewProperties'>)
# ('arm', <class '__main__.NewProperties'>)

Метаклассы получают плохую репутацию - иногда заслуженно - для добавления сложности.Не используйте их, когда подойдет более простой подход.Но для сокращения шаблонов в таких случаях они являются отличным инструментом.

...