如何循环定义三个方法?
How to define three methods circularly?
我有一个抽象 class,其中包含三种方法,它们在某种意义上是等价的 - 它们都可以使用一些昂贵的转换函数相互定义。我希望能够编写一个派生的 class ,它只需要覆盖其中一个方法并自动获取另外两个方法。示例
class FooBarBaz(object):
def foo(self, x):
return foo_from_bar(self.bar(x))
# OR return foo_from_baz(self.baz(x))
def bar(self, x):
return bar_from_foo(self.foo(x))
# OR return bar_from_baz(self.baz(x))
def baz(self, x):
return baz_from_bar(self.bar(x))
# OR return baz_from_foo(self.foo(x))
class Derived1(FooBarBaz):
def bar(self, x):
return 5
# at this point foo = foo_from_bar(5) and
# baz = baz_from_bar(5), which is what I wanted
class Derived2(FooBarBaz):
def foo(self, x):
return 6
# at this point bar = bar_from_foo(6) and
# baz = baz_from_bar(bar_from_foo(6)),
# which is not ideal, but still works
class Derived3(FooBarBaz):
def baz(self, x):
return 7
# at this point foo and bar remain defined
# in terms of each other, which is a PROBLEM
我知道我可以明确地告诉每个派生的 class 使用哪些转换。我想知道父级 class 是否有办法在不修改子级的情况下自行解决这个问题。
我建议在一个干净的圆圈中定义转换,这样重新定义一个方法就会打破这个圆圈。即:
class FooBarBaz(object):
def foo(self, x):
return foo_from_baz(self.baz(x))
def bar(self, x):
return bar_from_foo(self.foo(x))
def baz(self, x):
return baz_from_bar(self.bar(x))
编辑同时对提出的同一问题发表评论:
这应该可行,但当然,有时您需要转换两次。为避免这种情况,您可以实施某种惰性评估,即函数首先生成一个知道如何评估自身的对象,但首先当您询问它的值时,该操作就会执行。那样的话,转换链可以在评估之前被简化,如果它很昂贵的话。
您可以求助于元编程技术,例如编写一个自动填充剩余方法的元类,或者使用内省依次查看 type(self).mro()
中的 类 以找出哪些方法已被调用覆盖。但是,这些选项对我来说完全属于 "too much magic" 类别,所以我会选择更简单的选项。
简单地将每个方法分成两个:一个是通用的,一个是实际的实现。派生 类 覆盖实际实现:
class FooBarBaz(object):
def foo_impl(self, x):
raise NotImplementedError
def foo(self, x):
try:
return self.foo_impl(x)
except NotImplementedError:
try:
return foo_from_bar(self.bar_impl(x))
except NotImplementedError:
return foo_from_baz(self.baz_impl(x))
# Similarly fo bar and baz
class Dervied(FooBarBaz):
def bar_impl(self, x):
return 5
公共逻辑也可以在装饰器中分解出来:
def first_implemented(func):
@functools.wraps
def wrapper(*args, **kwargs):
for f in func(*args, **kwargs):
try:
return f()
except NotImplementedError:
pass
raise NotImplementedError
return wrapper
class FooBarBaz(object):
def foo_impl(self, x):
raise NotImplementedError
@first_implemented
def foo(self, x):
yield lambda: self.foo_impl(x)
yield lambda: foo_from_bar(self.bar_impl(x))
yield lambda: foo_from_baz(self.baz_impl(x))
我实际上是在复制。我发布我的答案以证明我为编写代码所付出的努力:
#!/usr/bin/env python
def foo_from_bar(x):
return 'foo_from_bar(%s)' % x
def bar_from_baz(x):
return 'bar_from_baz(%s)' % x
def baz_from_foo(x):
return 'baz_from_foo(%s)' % x
class FooBarBaz(object):
def foo(self, x):
return foo_from_bar(self.bar(x))
def bar(self, x):
return bar_from_baz(self.baz(x))
def baz(self, x):
return baz_from_foo(self.foo(x))
class Derived1(FooBarBaz):
def bar(self, x):
return 5
class Derived2(FooBarBaz):
def foo(self, x):
return 6
class Derived3(FooBarBaz):
def baz(self, x):
return 7
d1 = Derived1()
d2 = Derived2()
d3 = Derived3()
def check(expr):
print expr, '->', eval(expr)
for i,d in enumerate([d1, d2, d3]):
print '--- d = Derived%d() ----' % (i+1)
check('d.foo(0)')
check('d.bar(0)')
check('d.baz(0)')
输出:
--- d = Derived1() ----
d.foo(0) -> foo_from_bar(5)
d.bar(0) -> 5
d.baz(0) -> baz_from_foo(foo_from_bar(5))
--- d = Derived2() ----
d.foo(0) -> 6
d.bar(0) -> bar_from_baz(baz_from_foo(6))
d.baz(0) -> baz_from_foo(6)
--- d = Derived3() ----
d.foo(0) -> foo_from_bar(bar_from_baz(7))
d.bar(0) -> bar_from_baz(7)
d.baz(0) -> 7
装饰师也可以做到这一点。沿着这些线的东西:
def define_missing(cls):
has_at_least_one = False
if hasattr(cls, 'foo'):
if not hasattr(cls, 'bar'):
cls.bar = lambda self, x: bar_from_foo(self.foo(x))
if not hasattr(cls, 'baz'):
cls.baz = lambda self, x: baz_from_foo(self.foo(x))
has_at_least_one = True
if hasattr(cls, 'bar'):
if not hasattr(cls, 'foo'):
cls.foo = lambda self, x: foo_from_bar(self.bar(x))
if not hasattr(cls, 'baz'):
cls.baz = lambda self, x: baz_from_bar(self.bar(x))
has_at_least_one = True
if hasattr(cls, 'baz'):
if not hasattr(cls, 'bar'):
cls.foo = lambda self, x: foo_from_baz(self.baz(x))
if not hasattr(cls, 'baz'):
cls.bar = lambda self, x: bar_from_baz(self.baz(x))
has_at_least_one = True
if not has_at_least_one:
raise TypeError("Class needs to implement at least one of the methods foo, bar, and baz.")
return cls
然后像这样使用它:
@define_missing
class Derived1(FooBarBaz):
def bar(self, x):
return 5
这是受 functools.total_ordering
装饰器的启发。
我有一个抽象 class,其中包含三种方法,它们在某种意义上是等价的 - 它们都可以使用一些昂贵的转换函数相互定义。我希望能够编写一个派生的 class ,它只需要覆盖其中一个方法并自动获取另外两个方法。示例
class FooBarBaz(object):
def foo(self, x):
return foo_from_bar(self.bar(x))
# OR return foo_from_baz(self.baz(x))
def bar(self, x):
return bar_from_foo(self.foo(x))
# OR return bar_from_baz(self.baz(x))
def baz(self, x):
return baz_from_bar(self.bar(x))
# OR return baz_from_foo(self.foo(x))
class Derived1(FooBarBaz):
def bar(self, x):
return 5
# at this point foo = foo_from_bar(5) and
# baz = baz_from_bar(5), which is what I wanted
class Derived2(FooBarBaz):
def foo(self, x):
return 6
# at this point bar = bar_from_foo(6) and
# baz = baz_from_bar(bar_from_foo(6)),
# which is not ideal, but still works
class Derived3(FooBarBaz):
def baz(self, x):
return 7
# at this point foo and bar remain defined
# in terms of each other, which is a PROBLEM
我知道我可以明确地告诉每个派生的 class 使用哪些转换。我想知道父级 class 是否有办法在不修改子级的情况下自行解决这个问题。
我建议在一个干净的圆圈中定义转换,这样重新定义一个方法就会打破这个圆圈。即:
class FooBarBaz(object):
def foo(self, x):
return foo_from_baz(self.baz(x))
def bar(self, x):
return bar_from_foo(self.foo(x))
def baz(self, x):
return baz_from_bar(self.bar(x))
编辑同时对提出的同一问题发表评论: 这应该可行,但当然,有时您需要转换两次。为避免这种情况,您可以实施某种惰性评估,即函数首先生成一个知道如何评估自身的对象,但首先当您询问它的值时,该操作就会执行。那样的话,转换链可以在评估之前被简化,如果它很昂贵的话。
您可以求助于元编程技术,例如编写一个自动填充剩余方法的元类,或者使用内省依次查看 type(self).mro()
中的 类 以找出哪些方法已被调用覆盖。但是,这些选项对我来说完全属于 "too much magic" 类别,所以我会选择更简单的选项。
简单地将每个方法分成两个:一个是通用的,一个是实际的实现。派生 类 覆盖实际实现:
class FooBarBaz(object):
def foo_impl(self, x):
raise NotImplementedError
def foo(self, x):
try:
return self.foo_impl(x)
except NotImplementedError:
try:
return foo_from_bar(self.bar_impl(x))
except NotImplementedError:
return foo_from_baz(self.baz_impl(x))
# Similarly fo bar and baz
class Dervied(FooBarBaz):
def bar_impl(self, x):
return 5
公共逻辑也可以在装饰器中分解出来:
def first_implemented(func):
@functools.wraps
def wrapper(*args, **kwargs):
for f in func(*args, **kwargs):
try:
return f()
except NotImplementedError:
pass
raise NotImplementedError
return wrapper
class FooBarBaz(object):
def foo_impl(self, x):
raise NotImplementedError
@first_implemented
def foo(self, x):
yield lambda: self.foo_impl(x)
yield lambda: foo_from_bar(self.bar_impl(x))
yield lambda: foo_from_baz(self.baz_impl(x))
我实际上是在复制
#!/usr/bin/env python
def foo_from_bar(x):
return 'foo_from_bar(%s)' % x
def bar_from_baz(x):
return 'bar_from_baz(%s)' % x
def baz_from_foo(x):
return 'baz_from_foo(%s)' % x
class FooBarBaz(object):
def foo(self, x):
return foo_from_bar(self.bar(x))
def bar(self, x):
return bar_from_baz(self.baz(x))
def baz(self, x):
return baz_from_foo(self.foo(x))
class Derived1(FooBarBaz):
def bar(self, x):
return 5
class Derived2(FooBarBaz):
def foo(self, x):
return 6
class Derived3(FooBarBaz):
def baz(self, x):
return 7
d1 = Derived1()
d2 = Derived2()
d3 = Derived3()
def check(expr):
print expr, '->', eval(expr)
for i,d in enumerate([d1, d2, d3]):
print '--- d = Derived%d() ----' % (i+1)
check('d.foo(0)')
check('d.bar(0)')
check('d.baz(0)')
输出:
--- d = Derived1() ----
d.foo(0) -> foo_from_bar(5)
d.bar(0) -> 5
d.baz(0) -> baz_from_foo(foo_from_bar(5))
--- d = Derived2() ----
d.foo(0) -> 6
d.bar(0) -> bar_from_baz(baz_from_foo(6))
d.baz(0) -> baz_from_foo(6)
--- d = Derived3() ----
d.foo(0) -> foo_from_bar(bar_from_baz(7))
d.bar(0) -> bar_from_baz(7)
d.baz(0) -> 7
装饰师也可以做到这一点。沿着这些线的东西:
def define_missing(cls):
has_at_least_one = False
if hasattr(cls, 'foo'):
if not hasattr(cls, 'bar'):
cls.bar = lambda self, x: bar_from_foo(self.foo(x))
if not hasattr(cls, 'baz'):
cls.baz = lambda self, x: baz_from_foo(self.foo(x))
has_at_least_one = True
if hasattr(cls, 'bar'):
if not hasattr(cls, 'foo'):
cls.foo = lambda self, x: foo_from_bar(self.bar(x))
if not hasattr(cls, 'baz'):
cls.baz = lambda self, x: baz_from_bar(self.bar(x))
has_at_least_one = True
if hasattr(cls, 'baz'):
if not hasattr(cls, 'bar'):
cls.foo = lambda self, x: foo_from_baz(self.baz(x))
if not hasattr(cls, 'baz'):
cls.bar = lambda self, x: bar_from_baz(self.baz(x))
has_at_least_one = True
if not has_at_least_one:
raise TypeError("Class needs to implement at least one of the methods foo, bar, and baz.")
return cls
然后像这样使用它:
@define_missing
class Derived1(FooBarBaz):
def bar(self, x):
return 5
这是受 functools.total_ordering
装饰器的启发。