Pergunta

Eu estou vindo de um fundo C #, onde este material é super fácil de tentar traduzir em Python para Maya.

Tem que' ser uma maneira melhor de fazer isso. Basicamente, eu estou olhando para criar uma classe Vector que simplesmente terá x, yez coordenadas, mas seria ideal se esta classe retornou uma tupla com todos os 3 coordenadas e se você pode editar os valores desta tupla através de x, y e z propriedades, de alguma forma.

Isto é o que eu tenho até agora, mas há deve ser uma maneira melhor de fazer isso do que usando uma instrução exec, certo? Eu odeio usar declarações exec.

class Vector(object):
    '''Creates a Maya vector/triple, having x, y and z coordinates as float values'''

    def __init__(self, x=0, y=0, z=0):
        self.x, self.y, self.z = x, y, z

    def attrsetter(attr):
        def set_float(self, value):
            setattr(self, attr, float(value))
        return set_float

    for xyz in 'xyz':
        exec("%s = property(fget=attrgetter('_%s'), fset=attrsetter('_%s'))" % (xyz, xyz, xyz))
Foi útil?

Solução

Editar: Eu modificado o código com a minha resposta um pouco mais de @ unutbu é original para simplificá-lo e fazer o que está sendo feito mais clara. Na versão mais recente, os de @staticmethod foram completamente eliminados e substituídos com one-liners aninhadas. A função externa e classe aninhada foram renomeados AutoFloatProperties e _AutoFloatProperties para refletir seu comportamento especializado de converter e armazenar os valores atribuídos como carros alegóricos. Apesar de tudo isso, @ unutbu é própria resposta revisto, utilizando um decorador de classe em vez de uma metaclasse é uma solução um pouco mais simples, embora os internos e uso são muito semelhantes.

def AutoFloatProperties(*props):
    '''metaclass'''
    class _AutoFloatProperties(type):
        # Inspired by autoprop (http://www.python.org/download/releases/2.2.3/descrintro/#metaclass_examples)
        def __init__(cls, name, bases, cdict):
            super(_AutoFloatProperties, cls).__init__(name, bases, cdict)
            for attr in props:
                def fget(self, _attr='_'+attr): return getattr(self, _attr)
                def fset(self, value, _attr='_'+attr): setattr(self, _attr, float(value))
                setattr(cls, attr, property(fget, fset))
    return _AutoFloatProperties

class Vector(object):
    '''Creates a Maya vector/triple, having x, y and z coordinates as float values'''
    __metaclass__ = AutoFloatProperties('x','y','z')
    def __init__(self, x=0, y=0, z=0):
        self.x, self.y, self.z = x, y, z # values converted to float via properties

if __name__=='__main__':
    v=Vector(1,2,3)
    print(v.x)
    # 1.0
    v.x=4
    print(v.x)
    # 4.0

Outras dicas

Se eu entendi sua pergunta, você quer algo assim?

class Vector(object):

    def __init__(self, x=0, y=0, z=0):
        self._x, self._y, self._z = x, y, z

    def setx(self, x): self._x = float(x)
    def sety(self, y): self._y = float(y)        
    def setz(self, z): self._z = float(z)     

    x = property(lambda self: float(self._x), setx)
    y = property(lambda self: float(self._y), sety)
    z = property(lambda self: float(self._z), setz)

Este usos _x, _y e _z para (internamente) armazenar os valores e os expõe recebidas através do uso de propriedade (com getters, setters); I abreviada dos getters 'usando uma declaração lambda.

Note que em Python seria muito comum para manipular esses valores (por exemplo: x, y, z) no próprio directamente (? Eu acho que você quer garantir os moldes flutuador explícitas) objeto

I podem ser interpretando mal a sua pergunta, mas eu acho que o que você quer já é feito para você em collections.namedtuple :

>>> from collections import namedtuple
>>> Vector = namedtuple('Vector', 'x y z')
>>> v = Vector(0, 0, 0)
>>> v
Vector(x=0, y=0, z=0)
>>> v.x = 10
>>> v
Vector(x=10, y=0, z=0)
>>> tuple(v)
(10, 0, 0)
>>> v._asdict()
{'x': 10, 'y': 0, 'z': 0}
>>>

Does que olhar sobre a direita?

Por vergonha, eu esqueci que tuplas são imutáveis. Amaldiçoa-me por não actualizar a partir do Python 2.5 para que eu pudesse ter realmente testado o código que eu escrevi. De qualquer forma, você pode querer algo bastante semelhante ao collections.namedtuple, a não ser mais como um namedlist hipotética. Ou você pode querer descartar essa idéia completamente e usar algo diferente. O ponto é que esta resposta estava errado, e eu gostaria de excluí-lo, a não ser que eu sinta obrigado às pessoas que me upvoted para corrigir o meu erro.

É isso que você está procurando?

class vector(object):
    def __init__(self, x,y,z):
        self.x = x
        self.y = y
        self.z = z

    # overload []
    def __getitem__(self, index):
        data = [self.x,self.y,self.z]
        return data[index]

    # overload set []
    def __setitem__(self, key, item):
        if (key == 0):
            self.x = item
        elif (key == 1):
            self.y = item
        elif (key == 2):
            self.z = item
        #TODO: Default should throw excetion

Esta é a maneira mais ingênua de fazê-lo. Tenho certeza que alguns guru Python virá junto desprezo pelo meu código e substituí-lo com um one-liner.

Exemplos deste código:

v = vector(1,2,3)
v[1] = 4
v[2] = 5

v.x = 1
v.z= 66

Editar: A minha resposta anterior tentou fazer um AutoProperties generalizadas metaclass que eu esperava que poderia ser de uso geral. Como @ de Martineau mostras resposta uma solução especializada para a classe Vector pode tornar as coisas mais simples.

Aqui está uma outra ideia nesse sentido (simplicidade especializada sobre a complexidade generalizada). Ele usa um decorador de classe (que eu acho que é um pouco mais simples de entender do que uma metaclasse) e de @ martineau ideia de simplificar os getters e setters com valores padrão:

def AutoProperties(*props):
    def _AutoProperties(cls):
        for attr in props:
            def getter(self,_attr='_'+attr):
                return getattr(self, _attr)
            def setter(self, value, _attr='_'+attr):
                setattr(self, _attr, float(value))
            setattr(cls,attr,property(getter,setter))
        return cls
    return _AutoProperties

@AutoProperties('x','y','z')
class Vector(object):
    '''Creates a Maya vector/triple, having x, y and z coordinates as float values'''
    def __init__(self, x=0, y=0, z=0):
        self._x, self._y, self._z = map(float,(x, y, z))

resposta Original: Aqui está uma maneira de evitar a repetição de código caldeira-plate ao definir muitas propriedades semelhantes.

Eu tentei fazer a solução razoavelmente geral, por isso pode ser útil para as pessoas em outras situações ao lado de um presente especial.

Para usá-lo você precisa fazer 2 coisas:

  1. Coloque

        __metaclass__=AutoProperties(('x','y','z'))
    

    no início da definição de sua classe. Você pode listar (como strings) como muitos atributos (por exemplo x, y, z) como desejar. AutoProperties irá transformá-los em propriedades.

  2. Sua classe, por exemplo, Vector, precisa definir staticmethods _auto_setter e _auto_getter. Eles tomam um argumento, o nome do atributo como uma string, e retornar a função setter ou getter, respectivamente, para esse atributo.

A ideia de utilizar metaclasses para configurar automaticamente propriedades vem do ensaio de Guido Rossum em propriedades e metaclasses . Lá, ele define uma metaclass autoprop semelhante ao que eu uso abaixo. A principal diferença é que AutoProperties espera que o usuário para definir getter e setter fábricas em vez de getters e setters definidas manualmente.

def AutoProperties(props):
    class _AutoProperties(type):
        # Inspired by autoprop (http://www.python.org/download/releases/2.2.3/descrintro/)
        def __init__(cls, name, bases, cdict):
            super(_AutoProperties, cls).__init__(name, bases, cdict)
            for attr in props:
                fget=cls._auto_getter(attr)
                fset=cls._auto_setter(attr)
                setattr(cls,attr,property(fget,fset))
    return _AutoProperties

class Vector(object):
    '''Creates a Maya vector/triple, having x, y and z coordinates as float values'''
    __metaclass__=AutoProperties(('x','y','z'))
    def __init__(self, x=0, y=0, z=0):
        # I assume you want the initial values to be converted to floats too.
        self._x, self._y, self._z = map(float,(x, y, z))
    @staticmethod
    def _auto_setter(attr):
        def set_float(self, value):
            setattr(self, '_'+attr, float(value))
        return set_float
    @staticmethod   
    def _auto_getter(attr):
        def get_float(self):
            return getattr(self, '_'+attr)
        return get_float

if __name__=='__main__':
    v=Vector(1,2,3)
    print(v.x)
    # 1.0
    v.x=4
    print(v.x)
    # 4.0

Eu realmente não entendi a pergunta. Você tem um vetor que descreve um ponto no espaço com 3 coordenadas. Sua implementação já permite que você altere os valores:

v = Vector()
v.x = 10 # now x is 10

por que deveria retornar uma tupla? Para que você usaria isto? Dito isto, uma tupla é imutável, portanto, não podem ser modificados, mas você pode usar uma lista. Alterar os números nessa lista não refletirá em Vector embora.

Se você precisa fazer para garantir o tipo é um float, considere setters de propriedade :

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        print "x set to ", value
        self._x = value

c = C()
c.x = 10

print c.x, c._x

Você pode fácil representar o seu vector de fazer isso:

def __repr__(self):
   return "(%.1f, %.1f, %.1f)" % (self.x, self.y, self.z)

Quando você faz métodos com __ ... __ é como @Override em Java.

Eu entendo que

  1. você quer ter um filtro que transforma os valores de entrada em carros alegóricos
  2. você não quer escrever a propriedade três vezes

Você pode usar o seguinte código:

class Vector(object):
    def __init__(self, x,y,z):
         self._x = x

def mangle(name):
return '_'+name

for name in ['x','y','z']:
    def set_xyz(self, value):
        self.__setattr__(mangle(name), float(value))
    def get_xyz(self):
        return self.__getattribute__(mangle(name))
    prop = property(get_xyz, set_xyz)
    setattr(Vector,name, prop)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top