5

我来自 C# 背景,在这个背景下,这些东西非常简单——试图翻译成 Python for Maya。

必须有更好的方法来做到这一点。基本上,我希望创建一个仅具有 x、y 和 z 坐标的 Vector 类,但如果该类返回具有所有 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))
4

8 回答 8

5

如果我正确理解你的问题,你想要这样的东西吗?

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 来(内部)存储传入的值并通过使用属性(使用 getter、setter)公开它们;我使用 lambda 语句缩写了“getters”。

请注意,在 Python 中,直接在对象本身上操作这些值(例如:x、y、z)是很常见的(我猜你想确保显式的浮点转换?)

于 2010-03-08T12:40:23.227 回答
4

编辑:我已经用@unutbu 的原始答案修改了代码,以简化它并使正在做的事情更清晰。在最新版本中,@staticmethod's 已被完全删除,取而代之的是嵌套的单行。外部函数和嵌套类已被重命名AutoFloatProperties_AutoFloatProperties以反映它们将分配的值转换和存储为浮点数的特殊行为。尽管如此,@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
于 2010-07-28T21:39:55.917 回答
3

我可能误读了您的问题,但我认为您想要的已经为您制作了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。或者你可能想完全放弃这个想法并使用不同的东西。关键是这个答案是错误的,我会删除它,除非我觉得有义务让那些支持我的人纠正我的错误。

于 2010-03-08T12:38:49.347 回答
2

这是你要找的吗?

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
于 2010-03-08T12:42:11.083 回答
2

编辑:我之前的回答试图制作一个通用的 AutoProperties 元类,我希望它可以通用。正如@martineau 的回答所示,专门针对该Vector课程的解决方案可以使事情变得更简单。

这是沿着这些思路的另一个想法(专门的简单性优于一般的复杂性)。它使用类装饰器(我认为它比元类更容易理解)和@martineau 使用默认值简化 getter 和 setter 的想法:

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

原始答案: 这是一种在定义许多相似属性时避免重复样板代码的方法。

我试图使解决方案合理通用,因此它可能对除此特定情况之外的其他情况下的人们有用。

要使用它,您需要做两件事:

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

    在你的类定义的开头。您可以根据需要列出(作为字符串)任意数量的属性(例如x, y, z)。AutoProperties会将它们变成属性。

  2. 你的类,例如Vector,需要定义 staticmethods_auto_setter_auto_getter. 它们接受一个参数,即作为字符串的属性名称,并分别为该属性返回 setter 或 getter 函数。

使用元类自动设置属性的想法来自 Guido Rossum 关于属性和元类的文章。他在那里定义了一个autoprop类似于我在下面使用的元类。主要区别在于AutoProperties期望用户定义 getter 和 setter工厂,而不是手动定义 getter 和 setter。

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
于 2010-03-08T18:59:44.860 回答
1

我真的不明白这个问题。你有一个向量,它用 3 个坐标描述空间中的一个点。您的实现已经允许您更改值:

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

为什么要返回一个元组?你会用它做什么?也就是说,元组是不可变的,因此无法修改,但您可以使用列表。但是,更改该列表中的数字不会反映在 Vector 中。

如果您确实需要确保类型是浮点数,请考虑属性设置器

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
于 2010-03-08T12:31:11.247 回答
0

你可以很容易地表示你的向量这样做:

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

当您使用方法时,__ ... __就像@Override在 Java 上一样。

于 2015-05-14T18:50:20.113 回答
-1

我明白那个

  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)
于 2010-03-08T12:46:41.043 回答