我可以把它写成包装器吗?

Can I write this as a wrapper?

我有以下代码:

import numpy as np

class Basis(object):

def __init__(self, dimension):
    self.dimension = dimension

def coord(self, c):
    if self.dimension <= 2:
        return c
    else:
        return c + [0]*(self.dimension-2)

@property
def zerocoord(self):
    return self.coord([0,0])                  

@property
def Dcoord(self):
    return self.coord([1,0])

@property
def Tcoord(self):
    return self.coord([0,1])

@property
def Xcoord(self):
    return self.coord([1./np.sqrt(2), 1./np.sqrt(2)])

@property
def Ycoord(self):
    return self.coord([-1./np.sqrt(2), 1./np.sqrt(2)])

其中所有属性基本上每个属性都在调用相同的方法coord。这是因为我提供的实际数组 coord[0,0], [1,0], [0,1] 等是固定的,但可能会根据实例属性 dimension.

进行扩展

我对 Python 有点陌生,但直觉上(也许天真地)我认为这可以写成包装器...所以类似于:

@property
def coord(self)

@coord
def Dcoord(self)

这会使代码更优雅。

有人可以帮我吗?

个人觉得代码已经很优雅了。 您不应该t/can将coord设为属性,因为:

  • 您将无法向其传递参数,因为 属性 意味着(计算的?)字段的 "getter"。 coord 表现为一个函数,因此应该是一个函数。

如果您真的只是想减少代码的大小,您可以尝试其他答案中的方法,但我认为这并不是真正必要的。


旁白:据我从您的用例中了解到,您希望允许用户使用他们选择的自定义坐标调用 coord?如果不是,您可以考虑通过将其重命名为 _coord.

来使其私有化

您可以将要传递给 coord 方法的 属性 名称及其各自的常量值放在一个元组序列中,然后使用循环相应地设置属性:

class Basis(object):

    def __init__(self, dimension):
        self.dimension = dimension

    def coord(self, c):
        if self.dimension <= 2:
            return c
        else:
            return c + [0]*(self.dimension-2)

for name, value in ('zerocoord', [0, 0]), ('Dcoord', [1, 0]), ('Tcoord', [0, 1]), ('Xcoord', [1./np.sqrt(2), 1./np.sqrt(2)]), ('Ycoord', [-1./np.sqrt(2), 1./np.sqrt(2)]):
    setattr(Basis, name, property(lambda self, value=value: self.coord(value)))

定义您自己的描述符 Coord,而不是使用 property

from __future__ import division
import numpy as np

class Coord(object):
    def __init__(self, p1, p2):
        self.foo = [p1, p2]

    def __get__(self, obj, type=None):
        if obj.dimension > 2:
            return self.foo + [0 for x in range(2, obj.dimension)]
        else:
            return self.foo


class Basis(object):
    def __init__(self, d):
        self.dimension = d

    zerocoord = Coord(0, 0)
    dcoord = Coord(1, 0)
    tcoord = Coord(0, 1)
    xcoord = Coord(1/np.sqrt(2), 1/np.sqrt(2))
    ycoord = Coord(-1/np.sqrt(2), -1/np.sqrt(2))

现在,确定每种类型坐标形状的逻辑嵌入在描述符本身中,而不是您的 class。

一些例子:

>>> Basis(1).dcoord
[1, 0]
>>> Basis(3).dcoord
[1, 0, 0]
>>> Basis(4).tcoord
[0, 1, 0, 0]

您可以通过这种方式摆脱大量样板代码:

import numpy as np

class Basis(object):

    def __init__(self, dimension):
        self.dimension = dimension

    def coord(self, c):
        return c if self.dimension <= 2 else (c + [0]*(self.dimension-2))

    def _coord_prop(loc):
        @property
        def prop(self):
            return self.coord(loc)
        return prop

    zerocoord = _coord_prop([0, 0])
    Dcoord = _coord_prop([1, 0])
    Tcoord = _coord_prop([0, 1])
    Xcoord = _coord_prop([1./np.sqrt(2), 1./np.sqrt(2)])
    Ycoord = _coord_prop([-1./np.sqrt(2), 1./np.sqrt(2)])

    del _coord_prop  # Only used inside class definition.

basis = Basis(2)
print(basis.zerocoord)  # -> [0, 0]
print(basis.Dcoord)     # -> [1, 0]
print(basis.Tcoord)     # -> [0, 1]
print(basis.Xcoord)     # -> [0.7071067811865475, 0.7071067811865475]
print(basis.Ycoord)     # -> [-0.7071067811865475, 0.7071067811865475]

您可以使用装饰器来包装这些方法,方法是为它们调用 coord 方法并将它们转换为属性,这样这些方法只需要 return 相关常量即可:

def coord_property(func):
    def wrapper(self):
        return self.coord(func(self))
    return property(wrapper)

class Basis(object):

    def __init__(self, dimension):
        self.dimension = dimension

    def coord(self, c):
        if self.dimension <= 2:
            return c
        else:
            return c + [0]*(self.dimension-2)

    @coord_property
    def zerocoord(self):
        return [0,0]                  

    @coord_property
    def Dcoord(self):
        return [1,0]

    @coord_property
    def Tcoord(self):
        return [0,1]

    @coord_property
    def Xcoord(self):
        return [1./np.sqrt(2), 1./np.sqrt(2)]

    @coord_property
    def Ycoord(self):
        return [-1./np.sqrt(2), 1./np.sqrt(2)]

您不能将值传递给 属性 getter,装饰器会很快变得笨拙。如果您至少使用 3.4,则可以使用 functools.partialmethod.

减少行数

但是,最好保持代码原样,因为 "Explicit is better than implicit"。

from functools import partialmethod

class BasisWrapped(object):

    def __init__(self, dimension):
        self.dimension = dimension

    def coord(self, c):
        if self.dimension <= 2:
            return c
        else:
            return c + [0]*(self.dimension-2)

    zerocoord = partialmethod(coord, [0, 0])
    d_coord = partialmethod(coord, [1, 0])
    t_coord = partialmethod(coord, [0, 1])
    x_coord = partialmethod(coord, [1./np.sqrt(2), 1./np.sqrt(2)])
    y_coord = partialmethod(coord, [-1./np.sqrt(2), 1./np.sqrt(2)])