繁体   English   中英

Python 向量类

[英]Python Vector Class

我来自 C# 背景,在那里这些东西非常简单 - 试图将其转换为适用于 Maya 的 Python。

必须有更好的方法来做到这一点。 基本上,我希望创建一个仅具有 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))

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

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 语句缩写了“getter”。

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

编辑:我已经用@unutbu 的原始答案修改了代码,以简化它并使正在做的事情更清楚。 在最新版本中, @staticmethod已完全消除并替换为嵌套的单行。 外部函数和嵌套类已重命名为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

我可能误读了您的问题,但我认为您想要的已经在collections.namedtuple为您制作:

 >>> from collections import namedtuple >>> Vector = namedtuple('Vector', 'xy z') >>> v = Vector(0, 0, 0) >>> v Vector(x=0, y=0, z=0) >>> vx = 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 或者你可能想完全放弃这个想法并使用不同的东西。 关键是这个答案是错误的,我会删除它,除非我觉得有义务给我投票的人纠正我的错误。

这是你要找的吗?

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

编辑:我之前的回答试图制作一个通用的 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'))

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

  2. 您的类,例如Vector ,需要定义静态方法_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

我真的不明白这个问题。 你有一个向量,它用 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

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

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

当您使用__ ... __执行方法时,就像 Java 上的@Override

我明白那个

  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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM