【问题标题】:Nice way to call a method of a property调用属性方法的好方法
【发布时间】:2016-09-04 07:57:57
【问题描述】:

想象一下下面的代码(单独看完全没用):

# define a property with additional methods
class P(property):
    def __init__(self, name):
        property.__init__(self, 
            fget=lambda self: self._get(name),
            fset=lambda self, v: self._set(name, v))
        self._name = name

    def some_fn(self):
        print('name: ' + self._name)

# define a class with two 'enhanced' properties
class C:
    p1 = P('p1')
    p2 = P('p2')

    def __init__(self):
        self._values = {}

    def _get(self, name):
        return self._values[name]

    def _set(self, name, v):
        self._values[name] = v

c = C()

c.p1 = 5
c.p2 = c.p1

print(c.p1, c.p2)

我只是用两个properties 创建了一个类C,它们有一个额外的方法some_fn()

现在的问题是:你不能通过写c.p1.some_fn() 轻松调用some_fn(),因为你会先评估c.p1,这会导致一些值不再提供该方法。

我试图找到一些在certain property 的上下文中调用some_fn 的变通方法/方法,这不是它的价值,但我还不开心。

我的目标很简单:

  • 我希望能够在没有样板的情况下读取/分配属性: c.p1 = c.p2 而不是 c.p1.set(c.p2.get())

  • 我调用额外方法/函数的方式必须易于读/写

  • 我想编写可以被pylintmypy等静态验证的代码,所以some_fn('c.p1') is not an option because it can't be checked whether 'c.p1' is a valid attribute of an existing objectc`。

  • some_fn 不必必须是一种方法。它可以是一个函数或任何其他在属性上下文中请求功能的方式

  • 我什至不需要真实 properties。任何其他写s.th的方式。 像 c.p1 == c.p2 一样(例如使用 __getattr__/__setattr__)也可以,只要 get/set 操作仍然可跟踪。

我收集了一些代码来说明我在说什么:

# ==== What I want to do ==============
c.p1.some_fn()              #  <-- this is what I want to write but
                            #      it's invalid since it evaluates to
                            #      5.some_fn()

some_fn(c.p1)               #  <-- something like this looks OK, too but
                            #      it evalueates to some_fn(5) (useless)

# ==== These are options that came to mind but I'm not happy with ======

getattr(C, 'p1').some_fn()  #  <-- this works but it is ugly

some_fn("c.p1")             #  <-- this is possible, too but I can't 
                            #      check integrity statically (pylint/mypy)

c.p1.value = c.p2.value     #  <-- this is a valid approach but it 
c.p1.some_fn()              #      increases

some_fn(c.p1)  # (again)    #  <-- This can acutally work if you `inspect`
                            #      the call stack inside `C._get()` but 
                            #      it's black magic and incredibly slow

with some_fn():             #  <-- this can work when `some_fn` changes 
    c.p1                    #      some global state which get's evaluated
                            #      inside `C._get()`

【问题讨论】:

  • 你想让some_fn这样的方法做什么?你为什么需要它们?您提出的使用 c.p1.value 之类的示例是唯一可以真正起作用的示例。您无法让 c.p1 评估为 5,同时还允许 c.p1.some_fn()。听起来您需要自定义分配给属性的 ,而不是属性对象本身。

标签: python methods properties call


【解决方案1】:

我的目标很简单:我希望能够在没有样板的情况下读取/分配属性:c.p1 = c.p2

如果这是这里的目标,那听起来您误解了属性,因为它们已经像那样工作了。

class C(object):
    @property
    def p1(self):
        # get value
    @p1.setter
    def p1(self, val):
        # set value

    @property
    def p2(self):
        # get value
    @p2.setter
    def p2(self, val):
        # set value

如果你有一个对象c = C(),你可以做c.p1 = c.p2,它就可以工作。将更多方法粘贴到 property 对象上是错误的方法。


如果你真的想将方法粘贴到属性上,请通过类检索属性:

C.p1.some_fn()

【讨论】:

  • 我不坚持使用属性,实际上我也没有需要给属性添加方法。我的目标是使用像 c.p1component_x.subcomponent_y.parameter_z 这样的表达式,我可以像使用本机数据类型一样使用它们,并且可以做像 observe(c.p1)add_dependency(c.p1, c.p2) 这样的事情(这些只是命名上的 操作 的示例树中的元素)
  • @frans:是的,这与 Python 的评估模型不符。它适用于 R 之类的东西,但不适用于 Python。鉴于您已经考虑过,我能想到的最好的办法是制作单独的 p1_linkp2_link 属性,这些属性评估为代表“该对象的 p1 属性”的东西,因此 observe(c.p1_link) 会观察到 c.p1
  • 目前我只是在寻找一个不太令人毛骨悚然的解决方法 - 我已经摆弄了inspect 方法,但最终太慢了(每次调用大约 8 毫秒)。我会试试你的方法 - 它不会触及实现的其余部分,因此它非常灵活
  • @frans:正如 user2357112 所说,您永远无法完全做到这一点。无论c.p1 计算结果如何,它在c.p1 + 2 等表达式中的计算结果都必须与observe(c.p1) 相同。我认为最简单的事情就是让你的属性返回带有value 属性或类似属性的对象。您输入c.p1.value 所花费的额外时间将远远少于您尝试使更复杂的解决方案发挥作用所花费的时间。
猜你喜欢
  • 1970-01-01
  • 2010-12-09
  • 2018-03-03
  • 2012-01-27
  • 2012-10-24
  • 1970-01-01
  • 2015-12-03
  • 2018-09-05
  • 2011-05-13
相关资源
最近更新 更多