Python向量类

发布于 2024-08-24 11:03:09 字数 716 浏览 5 评论 0原文

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

I'm coming from a C# background where this stuff is super easy—trying to translate into Python for Maya.

There's gotta' be a better way to do this. Basically, I'm looking to create a Vector class that will simply have x, y and z coordinates, but it would be ideal if this class returned a tuple with all 3 coordinates and if you could edit the values of this tuple through x, y and z properties, somehow.

This is what I have so far, but there must be a better way to do this than using an exec statement, right? I hate using exec statements.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

你曾走过我的故事 2024-08-31 11:03:09

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

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 (内部)存储传入值并通过使用 property(带有 getter、setter);我使用 lambda 语句缩写了“getters”。

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

If I understand your question correctly, you want something like this ?

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)

This uses _x, _y and _z to (internally) store the incoming values and exposes them via the use of property (with getters, setters); I abbreviated the 'getters' using a lambda statement.

Note that in Python it would be very common to manipulate these values (say: x, y, z) on the object itself directly (I guess you want ensure the explicit float casts?)

裂开嘴轻声笑有多痛 2024-08-31 11:03:09

编辑:我已经用我的答案对@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

Edit: I've modified the code with my answer a bit more from @unutbu's original to simplify it and make what is being done clearer. In the latest version, the @staticmethod's have been eliminated altogether and replaced with nested one-liners. The outer function and nested class have been renamed AutoFloatProperties and _AutoFloatProperties to reflect their specialized behavior of converting and storing the values assigned as floats. Despite all this, @unutbu's own revised answer using a class decorator instead of a metaclass is a slightly simpler solution, although the internals and usage are very similar.

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
眼眸印温柔 2024-08-31 11:03:09

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

I may be misreading your question, but I think what you want is already made for you in 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 that look about right?

For shame, I forgot that tuples are immutable. Curse me for not upgrading from Python 2.5 so I could have actually tested the code I wrote. Anyway, you may want something quite similar to collections.namedtuple, except more like a hypothetical namedlist. Or you may want to discard that idea entirely and use something different. The point is that this answer was wrong, and I would delete it, except I feel obligated to the people who upvoted me to correct my mistake.

各自安好 2024-08-31 11:03:09

这是您要找的吗?

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

Is this what you're looking for?

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

This is the most naive way of doing it. I'm sure some Python guru will come along sneer at my code and replace it with a one-liner.

Examples of this code:

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

v.x = 1
v.z= 66
淡淡的优雅 2024-08-31 11:03:09

编辑:我之前的回答试图创建一个通用的 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

Edit: My previous answer tried to make a generalized AutoProperties metaclass which I hoped could be of general use. As @martineau's answer shows a solution specialized to the Vector class can make things simpler.

Here's another idea along those lines (specialized simplicity over generalized complexity). It uses a class decorator (which I think is slightly simpler to understand than a metaclass) and @martineau's idea of simplifying the getters and setters with default values:

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

Original answer:
Here is a way to avoid repeating boiler-plate code when defining many similar properties.

I've tried to make the solution reasonably general, so it might be of use to people in other situations beside this particular one.

To use it you need to do 2 things:


  1. Put

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

    at the beginning of the definition of your class. You can list (as strings) as many attributes (e.g. x,y,z) as you wish. AutoProperties will turn them into properties.

  2. Your class, e.g. Vector, needs to define staticmethods _auto_setter and _auto_getter. They take one argument, the attribute name as a string, and return the setter or getter function, respectively, for that attribute.

The idea of using metaclasses to automatically set up properties comes from Guido Rossum's essay on properties and metaclasses. There he defines an autoprop metaclass similar to what I use below. The main difference is that AutoProperties expects the user to define getter and setter factories instead of manually defined getters and setters.

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
梦醒时光 2024-08-31 11:03:09

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

I don't really understand the question. You have a Vector which describes a point in space with 3 coordinates. Your implementation already allows you to change the values:

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

why should it return a tuple? What would you use it for? That said, a tuple is immutable so can't be modified, but you could use a list. Changing the numbers in that list will not reflect in Vector though.

If you do need to ensure the type is a float, consider property setters:

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
老旧海报 2024-08-31 11:03:09

您可以轻松地表示这样做的向量:

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

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

You can easy represent your vector doing this:

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

When you do methods with __ ... __ is like @Override on Java.

洒一地阳光 2024-08-31 11:03:09

我知道

  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)

I understand that

  1. you want to have a filter that transform the input values into floats
  2. you don't want to write the property three times

You could use the following code:

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文