如何以Pythonic方式在仅含Getter方法的类中使用match/case语句?
问题描述
我使用一些非自研的类,这些类仅提供Getter方法(如a()、b())而非属性/特性来访问内部数据。例如有如下类定义:
class Foo: def __init__(self, a, b) -> None: self._a = a self._b = b def a(self): return self._a def b(self): return self._b def c(self): return self._a + self._b
我尝试用Python的match/case语法实现如下逻辑,但代码无法运行——因为case语句匹配对象时,要求命名参数对应类的属性/特性,而非Getter方法:
foo_list = [ Foo(1,0), Foo(0,1), Foo(1,2), Foo(3,3), Foo(3,2), ] for foo in foo_list: match foo: case Foo(a=a, b=0): print(f'first a = {a}, b = {foo.b}') case Foo(a=0, b=b): print(f'second a = {foo.a}, b = {b}') case Foo(a=1, b=b): print(f'third a = {foo.a}, b = {b}') case Foo(a=a, b=3): print(f'forth a = {a}, b = {foo.b}') case Foo(a=a, b=b, c=5): print(f'fifth a = {a}, b = {b}, c = {foo.c}')
请问如何以Pythonic的方式解决这个问题?
解决方案
方法1:使用守卫条件(Guard Clauses)
这是最直接的方案,无需额外封装,直接在case后添加if条件调用Getter方法,完全贴合原逻辑的匹配优先级:
for foo in foo_list: match foo: case _ if foo.a() == 1 and foo.b() == 0: print(f'first a = {foo.a()}, b = {foo.b()}') case _ if foo.a() == 0: b_val = foo.b() print(f'second a = {foo.a()}, b = {b_val}') case _ if foo.a() == 1: b_val = foo.b() print(f'third a = {foo.a()}, b = {b_val}') case _ if foo.b() == 3: a_val = foo.a() print(f'forth a = {a_val}, b = {foo.b()}') case _ if foo.c() == 5: a_val = foo.a() b_val = foo.b() print(f'fifth a = {a_val}, b = {b_val}, c = {foo.c()}')
方法2:编写专属适配类
如果需要多次对这类对象使用match/case,可以封装一个适配器,将Getter方法转为只读属性,保留match/case的结构化匹配语法:
class FooAdapter: def __init__(self, foo_obj): self._foo = foo_obj @property def a(self): return self._foo.a() @property def b(self): return self._foo.b() @property def c(self): return self._foo.c() # 使用时先包装原对象 for foo in foo_list: adapted = FooAdapter(foo) match adapted: case FooAdapter(a=a, b=0): print(f'first a = {a}, b = {adapted.b}') case FooAdapter(a=0, b=b): print(f'second a = {adapted.a}, b = {b}') case FooAdapter(a=1, b=b): print(f'third a = {adapted.a}, b = {b}') case FooAdapter(a=a, b=3): print(f'forth a = {a}, b = {adapted.b}') case FooAdapter(a=a, b=b, c=5): print(f'fifth a = {a}, b = {b}, c = {adapted.c}')
方法3:通用Getter适配器
如果需要适配多个类似的Getter风格类,可以写一个通用适配器,通过__getattr__动态将方法调用转为属性访问:
class GetterAdapter: def __init__(self, obj): self._obj = obj def __getattr__(self, name): # 尝试调用对应名称的Getter方法 try: getter = getattr(self._obj, name) return getter() except AttributeError: raise AttributeError(f"'{type(self._obj).__name__}' object has no attribute '{name}'") # 适配任意Getter风格的类 for foo in foo_list: adapted = GetterAdapter(foo) match adapted: case GetterAdapter(a=a, b=0): print(f'first a = {a}, b = {adapted.b}') case GetterAdapter(a=0, b=b): print(f'second a = {adapted.a}, b = {b}') case GetterAdapter(a=1, b=b): print(f'third a = {adapted.a}, b = {b}') case GetterAdapter(a=a, b=3): print(f'forth a = {a}, b = {adapted.b}') case GetterAdapter(a=a, b=b, c=5): print(f'fifth a = {a}, b = {b}, c = {adapted.c}')
内容的提问来源于stack exchange,提问作者nPn
相关产品推荐
相关产品推荐

