我可以写为包装纸吗?

时间:2018-10-19 17:43:58

标签: python wrapper python-decorators

我有以下代码:

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)

这会使代码更加优雅。

有人可以帮我吗?

6 个答案:

答案 0 :(得分:2)

您可以将属性名称及其相应的常数值以元组序列的形式传递给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))) 方法,然后使用循环相应地设置属性:

{{1}}

答案 1 :(得分:2)

定义自己的描述符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))

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

一些例子:

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

答案 2 :(得分:2)

通过这种方式,您可以摆脱很多样板代码:

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]

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

您不能将值传递给属性获取器,装饰器将变得笨拙。如果您使用的是租约3.4,则可以使用functools.partialmethod减少行数。

但是,最好按原样保留代码,因为“显式比隐式更好”。

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

答案 5 :(得分:0)

就我个人而言,我认为代码已经很不错了。 您不应/不能将coord设为财产,因为:

  • 您将无法将参数传递给它,因为属性是(计算得出的)字段的“获取器”。 coord表现为一种功能,因此应该是一个。

如果您真的只是想减少代码的大小,则可以尝试其他答案中的一种方法,尽管我认为这并不是必须的。


此外:据您所知,您想允许用户使用自己选择的自定义坐标来呼叫coord吗?如果没有,则可以考虑将其重命名为_coord