如何在python中向类添加名称为字符串的函数?
我有几个类,它们的方法都遵循相同的结构。为了这个例子,让我们假设它是如何在python中向类添加名称为字符串的函数?,python,python-2.7,class,Python,Python 2.7,Class,我有几个类,它们的方法都遵循相同的结构。为了这个例子,让我们假设它是 class Foo: def say_hello(self,a,b,c,d) : print("hello" + a + b + c + d) def say_moo(self,a,b,c,d) : print("moo" + a + b + c + d) def say_foo(self,a,b,c,d) : print("foo" + a + b + c + d) # more of the
class Foo:
def say_hello(self,a,b,c,d) : print("hello" + a + b + c + d)
def say_moo(self,a,b,c,d) : print("moo" + a + b + c + d)
def say_foo(self,a,b,c,d) : print("foo" + a + b + c + d)
# more of the same pattern...
class Bar:
def say_hello(self,a,b,c,d) : print("hello" + a + b + c + d)
def say_bar(self,a,b,c,d) : print("bar" + a + b + c + d)
def say_boo(self,a,b,c,d) : print("boo" + a + b + c + d)
#more classes of the same pattern...
拥有这些方法是一项要求,因此简单地使用def sayx(self,x,a,b,c,d):print(x+a+b+c+d)
并不是一个解决方案
我想避免重复我自己那么多,我正在寻找一种方法来写一些关于
class Foo :
def SOME_MAGIC("hello")
def SOME_MAGIC("moo")
def SOME_MAGIC("foo")
这将产生与上述Foo
等效的结果。搜索主题时,我总是接触到一些元类,但它们总是伴随着便条:“如果你想知道你是否需要它们,你不需要。”。因此,我显然不需要它们。我需要什么 你在找什么
或者,您可以使用: 测试: 好处是您只需定义字符串一次。缺点是可读性不太明显 如果类之间的
say()
是通用的,您甚至可以这样做:
from functools import partialmethod
def add_say(cls, *cmds:str):
def say(self, x, a, b, c, d):
print(x, a, b, c, d)
# ... do something with self if you need ...
for cmd in cmds:
setattr(cls, f'say_{cmd}', partialmethod(say, cmd))
class Foo:
def __init__(self):
add_say(Foo, 'hello', 'moo', 'boo')
class Bar:
def __init__(self):
add_say(Bar, 'hello', 'bar', 'baz')
测试:
代码需要进行调整,以实现Python 3的兼容性
我故意避免使用functools助手。我不鼓励上面的代码,因为它会使堆栈跟踪更加详细,破坏静态分析工具,使动态内省更加困难,并且通常比不太动态的方法更容易混淆。在这里,您可以利用继承。使类
Foo
和Bar
从另一个单独的类继承一些魔法
,如BarStool
@hampusralson大多数方法在Foo
和Bar
(以及其他)中不同,只有少数例外。我知道我可以将say_hello
移动到一个基类,但我知道如何在这里使用继承,我希望我只能写一次hello
,但这已经接近了Enough标记了他们的问题python2.7,我认为格式字符串或类型注释不兼容。每次创建实例时,执行任一解决方案都会创建并绑定新的分部函数。在第一个解决方案中,这些方法根本不存在于类中。在我看来,这两种解决方案都是不好的。对于后一种解决方案,在创建实例之前,该方法不会存在于dict类中。这可能会导致意外的问题:无异常执行Foo。say_hello(Foo(),*range(4))
将取决于是否在它之前创建了Foo的实例。很公平,我没有捕获python2.7标记。然而,我认为这里最明显、最清晰的解决方案就是写出所有的say_which
方法,因为它在整个代码库中更加清晰、可读和可管理。在我看来,强制使用一种不同的say_which
方法来做相同的事情是一个需要重新考虑的设计决策,但这显然是OP无法控制的。
from functools import partial
class Foo:
def __init__(self):
cmds = ('hello', 'moo', 'boo') # only define your strings once
for c in cmds:
setattr(self, f'say_{c}', partial(self.say, c))
def say(self, x, a, b, c, d):
print(x + a + b + c + d)
>>> f = Foo()
>>> f.say_hello(*'abcd')
helloabcd
>>> f.say_boo(*'1234')
boo1234
from functools import partialmethod
def add_say(cls, *cmds:str):
def say(self, x, a, b, c, d):
print(x, a, b, c, d)
# ... do something with self if you need ...
for cmd in cmds:
setattr(cls, f'say_{cmd}', partialmethod(say, cmd))
class Foo:
def __init__(self):
add_say(Foo, 'hello', 'moo', 'boo')
class Bar:
def __init__(self):
add_say(Bar, 'hello', 'bar', 'baz')
>>> f = Foo()
>>> f.say_boo(*'abcd')
boo a b c d
>>> b = Bar()
>>> b.say_baz(*'dude')
baz d u d e
from __future__ import print_function
class SayMetaClass( type ):
def __new__( mcls, name, bases, dict_ ):
say_template = lambda value: lambda self, a, b, c, d: print( value + a + b + c + d )
if "SAY_WHAT" in dict_:
for value in dict_[ "SAY_WHAT" ]:
method_name = "say_{value}".format( value=value )
if method_name in dict_:
raise ValueError( "{method_name} is already defined in the class!".format( method_name=method_name ) )
dict_[ method_name ] = say_template( value )
del dict_[ "SAY_WHAT" ]
return super( SayMetaClass, mcls ).__new__( mcls, name, bases, dict_ )
class Foo( object ):
__metaclass__ = SayMetaClass
SAY_WHAT = [ "hello", "moo", "foo" ]
class Bar( object ):
__metaclass__ = SayMetaClass
SAY_WHAT = [ "hello", "bar", "boo" ]
class Baz( Foo, Bar ):
pass
args = "1234"
f = Foo()
f.say_hello( *args )
f.say_moo( *args )
f.say_foo( *args )
b = Baz()
b.say_hello( *args )
b.say_moo( *args )
b.say_bar( *args )