Векторный класс Python - PullRequest
       20

Векторный класс Python

6 голосов
/ 08 марта 2010

Я пришел из C # фона, где это очень просто - пытаюсь перевести на Python для Maya.

Должен быть лучший способ сделать это. По сути, я пытаюсь создать класс Vector, который будет просто иметь координаты x, y и z, но было бы идеально, если бы этот класс возвращал кортеж со всеми 3 координатами и если вы могли бы редактировать значения этого кортежа через x свойства y и z, как угодно.

Это то, что я имею до сих пор, но должен быть лучшим способом сделать это, чем использование оператора exec, верно? Я ненавижу использовать выражения 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))

Ответы [ 8 ]

5 голосов
/ 08 марта 2010

Если я правильно понимаю ваш вопрос, вы хотите что-то подобное?

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)

Это использует _x, _y и _z для (внутреннего) хранения входящих значений и предоставляет их с помощью свойства (с геттерами, сеттерами); Я сократил «получатели», используя лямбда-выражение.

Обратите внимание, что в Python очень часто манипулируют этими значениями (скажем, x, y, z) непосредственно на самом объекте (я полагаю, вы хотите обеспечить явное приведение типов с плавающей точкой?) *

4 голосов
/ 29 июля 2010

Редактировать: Я изменил код с моим ответом немного больше от оригинала @ unutbu, чтобы упростить его и сделать более понятным то, что делается. В последней версии @staticmethod были полностью исключены и заменены вложенными однострочниками. Внешняя функция и вложенный класс были переименованы в AutoFloatProperties и _AutoFloatProperties, чтобы отразить их специализированное поведение преобразования и сохранения значений, присвоенных как float. Несмотря на все это, собственный пересмотренный ответ @ unutbu, использующий декоратор класса вместо метакласса, представляет собой несколько более простое решение, хотя внутренняя структура и использование очень похожи.

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
3 голосов
/ 08 марта 2010

Возможно, я неверно истолковал ваш вопрос, но я думаю, что то, что вы хотите, уже сделано для вас в 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}
>>>

Это выглядит правильно?

К стыду, я забыл, что кортежи неизменны. Прокляните меня за то, что я не обновляю Python 2.5, чтобы я мог на самом деле протестировать код, который я написал. В любом случае, вы можете захотеть что-то очень похожее на collections.namedtuple, за исключением более похожего на гипотетический namedlist. Или вы можете полностью отказаться от этой идеи и использовать что-то другое. Дело в том, что этот ответ был неправильным, и я бы его удалил, за исключением того, что чувствую себя обязанным людям, которые проголосовали против меня, чтобы исправить мою ошибку.

2 голосов
/ 08 марта 2010

Редактировать: В моем предыдущем ответе я попытался создать обобщенный метакласс AutoProperties, который, как я надеялся, может быть полезен. Как показывает ответ @ martineau, решение, специализированное для класса Vector, может упростить задачу.

Вот еще одна идея в этом направлении (специализированная простота над обобщенной сложностью). Он использует декоратор класса (который, я думаю, немного проще для понимания, чем метакласс) и идею @ martineau по упрощению методов получения и установки с использованием значений по умолчанию:

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))

Оригинальный ответ: Вот способ избежать повторения кода котельной пластины при определении многих подобных свойств.

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

Чтобы использовать его, вам нужно сделать 2 вещи:

  1. Помещенный
        __metaclass__=AutoProperties(('x','y','z'))
    

    в начале определения вашего класса. Вы можете перечислить (в виде строк) столько атрибутов (например, x, y, z), сколько пожелаете. AutoProperties превратит их в свойства.

  2. Ваш класс, например Vector, необходимо определить статические методы _auto_setter и _auto_getter. Они принимают один аргумент, имя атрибута как строку, и возвращают функцию установки или получения, соответственно, для этого атрибута.

Идея использования метаклассов для автоматической настройки свойств взята из эссе Гвидо Россума о свойствах и метаклассах . Там он определяет метакласс autoprop, аналогичный тому, который я использую ниже. Основное отличие состоит в том, что AutoProperties ожидает, что пользователь определит геттер и установщик фабрики вместо определенных вручную геттеров и сеттеров.

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
2 голосов
/ 08 марта 2010

Это то, что вы ищете?

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

Это самый наивный способ сделать это. Я уверен, что какой-нибудь гуру Python придет в насмешку над моим кодом и заменит его однострочным.

Примеры этого кода:

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

v.x = 1
v.z= 66
1 голос
/ 08 марта 2010

Я не очень понимаю вопрос. У вас есть Вектор, который описывает точку в пространстве с 3 координатами. Ваша реализация уже позволяет вам изменять значения:

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

почему он должен возвращать кортеж? Что бы вы использовали для этого? Тем не менее, кортеж является неизменным, поэтому не может быть изменен, но вы можете использовать список. Однако изменение чисел в этом списке не будет отражаться в векторе.

Если вам нужно убедиться, что тип является плавающим, рассмотрите установщиков свойств :

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
0 голосов
/ 14 мая 2015

Вы можете легко представить свой вектор следующим образом:

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

Когда вы делаете методы с __ ... __, это похоже на @Override на Java.

0 голосов
/ 08 марта 2010

Я понимаю, что

  1. вы хотите иметь фильтр, который преобразует входные значения в числа с плавающей запятой
  2. Вы не хотите писать свойство три раза

Вы можете использовать следующий код:

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)
...