我问这个问题是因为对这个答案的评论主题进行了讨论.我90%的方式来绕过它.
In [1]: class A(object): # class named 'A'
...: def f1(self): pass
...:
In [2]: a = A() # an instance
Run Code Online (Sandbox Code Playgroud)
f1 存在三种不同的形式:
In [3]: a.f1 # a bound method
Out[3]: <bound method a.f1 of <__main__.A object at 0x039BE870>>
In [4]: A.f1 # an unbound method
Out[4]: <unbound method A.f1>
In [5]: a.__dict__['f1'] # doesn't exist
KeyError: 'f1'
In [6]: A.__dict__['f1'] # a function
Out[6]: <function __main__.f1>
Run Code Online (Sandbox Code Playgroud)
绑定方法,未绑定方法和函数对象之间的区别是什么,所有这些都由f1描述?如何调用这三个对象?他们怎么能相互转化?关于这些东西的文档很难理解.
基于我对Python数据模型的理解,特别是"实例方法"小节,每当您读取其值为"用户定义函数"类型的属性时,一些魔法就会启动并获得绑定实例方法而不是实际方法,原始功能.这就是为什么self在调用方法时没有显式传递参数的原因.
但是,我希望能够用具有相同签名的函数替换对象的方法:
class Scriptable:
def __init__(self, script = None):
if script is not None:
self.script = script # replace the method
def script(self):
print("greetings from the default script")
>>> scriptable = Scriptable()
>>> scriptable.script()
greetings from the default script
>>> def my_script(self):
... print("greetings from my custom script")
...
>>> scriptable = Scriptable(my_script)
>>> scriptable.script()
Traceback (most recent call last):
...
TypeError: script() takes exactly 1 positional argument (0 given)
Run Code Online (Sandbox Code Playgroud)
我正在创建一个实例Scriptable,并script使用单个参数将其属性设置为用户定义的函数,就像在类中定义的那样.因此,当我读取scriptable.script …
考虑一个简单的函数
def increment(self):
self.count += 1
Run Code Online (Sandbox Code Playgroud)
它通过Cython运行并编译成扩展模块.假设现在我想把这个函数作为一个类的方法.例如:
class Counter:
def __init__(self):
self.count = 0
from compiled_extension import increment
Counter.increment = increment
Run Code Online (Sandbox Code Playgroud)
现在这不起作用,因为C级别的调用约定将被打破.例如:
>>> c = Counter()
>>> c.increment()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: increment() takes exactly one argument (0 given)
Run Code Online (Sandbox Code Playgroud)
但是在Python 2中,我们可以通过执行以下操作将函数转换为未绑定的方法:
Counter.increment = types.MethodType(increment, None, Counter)
Run Code Online (Sandbox Code Playgroud)
我怎样才能在Python 3中完成同样的事情?
一种简单的方法是使用纤薄的包装:
from functools import wraps
def method_wraper(f):
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
return wraps(f)(wrapper)
Counter.increment = method_wrapper(increment)
Run Code Online (Sandbox Code Playgroud)
有更有效的方法吗?
为什么python描述符中的__get__方法接受所有者类作为它的第三个参数?你能给出一个使用它的例子吗?
第一个参数(self)是不言而喻的,第二个(instances)在通常显示的描述符模式的上下文中是有意义的(如下所示),但我从未真正看到owner使用过的第三个().有人可以解释用例是什么吗?
仅仅通过参考和促进答案,这是我见过的描述符的典型用法:
class Container(object):
class ExampleDescriptor(object):
def __get__(self, instance, owner):
return instance._name
def __set__(self, instance, value):
instance._name = value
managed_attr = ExampleDescriptor()
Run Code Online (Sandbox Code Playgroud)
鉴于instance.__class__可用,我能想到的是显式传递类与直接从类而不是实例(ex Container.managed_attr)访问描述符有关.即便如此,我还不清楚__get__在这种情况下会做些什么.
我有一个入口点函数main在一个对象上调用它,我想保持未被模拟,因为它在对象上调用了几个其他方法:
class Thing(object):
def main(self):
self.alpha()
self.bravo()
def alpha(self):
self.charlie()
def bravo(self):
raise TypeError("Requires Internet connection!")
def charlie(self):
raise Exception("Bad stuff happens here!")
Run Code Online (Sandbox Code Playgroud)
这很简单,可以手动模拟:
thing = Thing()
thing.alpha = MagicMock()
thing.bravo = MagicMock()
Run Code Online (Sandbox Code Playgroud)
我可以测试以确保alpha和bravo都被调用一次,我可以在alpha和bravo中设置副作用以确保它们被处理等等.
我担心的是,如果代码定义发生变化并且有人添加了一个charlie调用main.它没有被嘲笑,所以现在会感觉到副作用(而且它们就像写入文件,连接到数据库,从Internet上获取东西,因此这个简单的异常不会提醒我现在的测试坏).
我的计划是验证我的模拟对象不会调用其他方法而不是我应该说的方法(或者引发测试异常).但是,如果我做这样的事情:
MockThing = create_autospec(Thing)
thing = Thing()
thing.main()
print thing.method_calls
# [calls.main()]
Run Code Online (Sandbox Code Playgroud)
然后main也被嘲笑,所以它没有其他方法.我怎么能模仿每个方法但主要的方法呢?(我想要method_calls [calls.alpha(), calls.bravo()]).
编辑:用于黑客答案
好吧,我有一个非常hacky解决方案,但我希望有一个比这更好的答案.基本上我从原始类重新绑定方法(Python绑定一个未绑定的方法)
MockThing = create_autospec(Thing)
thing = MockThing()
thing.main = Thing.ingest.__get__(thing, Thing)
thing.main()
print thing.method_calls
# [calls.alpha(), …Run Code Online (Sandbox Code Playgroud) 考虑一下Python中策略模式的这个例子(改编自这里的例子).在这种情况下,替代策略是一种功能.
class StrategyExample(object):
def __init__(self, strategy=None) :
if strategy:
self.execute = strategy
def execute(*args):
# I know that the first argument for a method
# must be 'self'. This is just for the sake of
# demonstration
print locals()
#alternate strategy is a function
def alt_strategy(*args):
print locals()
Run Code Online (Sandbox Code Playgroud)
以下是默认策略的结果.
>>> s0 = StrategyExample()
>>> print s0
<__main__.StrategyExample object at 0x100460d90>
>>> s0.execute()
{'args': (<__main__.StrategyExample object at 0x100460d90>,)}
Run Code Online (Sandbox Code Playgroud)
在上面的例子中s0.execute是一个方法(不是普通的vanilla函数),因此args,正如预期的那样,第一个参数是self.
以下是替代策略的结果.
>>> s1 = …Run Code Online (Sandbox Code Playgroud) 我正在尝试SomeClass从导入的包中对一个方法进行monkeypatch :
from somepackage import SomeClass
def newmethod(obj, node, **kwargs):
""" """
SomeClass.oldmethod = newmethod
Run Code Online (Sandbox Code Playgroud)
在哪里obj和node在默认调用签名SomeClass.oldmethod:
class SomeClass(object):
def oldmethod(obj, node):
""" """
Run Code Online (Sandbox Code Playgroud)
我知道monkeypatching不是一个好习惯,但我们需要一个解决方法,同时我们解决了一些无法解决的问题.上面的方法很有效,但是我们想用部分函数来做这件事.例如:
from functools import partial
newmethod_a = partial(newmethod, foo='a')
newmethod_b = partial(newmethod, foo='b')
Run Code Online (Sandbox Code Playgroud)
调用部分函数是因为我们需要传递不同的**kwargs.但是当我现在尝试超载时:
SomeClass.oldmethod = newmethod_a
Run Code Online (Sandbox Code Playgroud)
我得到一个与传递的参数数量相关的错误,但它非常特定于我的问题所以粘贴它可能没有帮助...我认为错误与oldmethod采取两个位置参数(obj, node)和我的部分的调用签名有关功能不传递到一基准obj和node正确.我尝试了不同的结构,如:
newmethod_a = partial(SomeClass.newmethod, foo='a')
Run Code Online (Sandbox Code Playgroud)
对不起,我无法生成一个最小的工作示例.我希望也许专家会从经验中认识到这个问题,并告诉我,如果我在尝试的范围内甚至是可能的partial.
谢谢
是否有可能在python中实现泛型方法处理程序,允许调用不存在的函数?像这样的东西:
class FooBar:
def __generic__method__handler__(.., methodName, ..):
print methodName
fb = FooBar()
fb.helloThere()
-- output --
helloThere
Run Code Online (Sandbox Code Playgroud) 我想动态设置实例方法的默认键参数。例如,与
class Module(object):
def __init__(self, **kargs):
set-default-key-args-of-method(self.run, kargs) # change run arguments
def run(self, **kargs):
print kargs
Run Code Online (Sandbox Code Playgroud)
我们会有:
m = Module(ans=42)
m.run.im_func.func_code.co_argcount # => 2
m.run.im_func.func_code.co_varnames # => ('self','ans','kargs')
m.run.im_func.func_defaults # => (42,)
m.run() # print {'ans':42}
Run Code Online (Sandbox Code Playgroud)
我尝试了一些带有 types.CodeType(我不太了解)的函数(不是方法)并让它工作(很好不会失败),但是添加的键参数没有显示在 kargs 中函数字典(它只打印 {})
必须仅对当前实例进行更改。实际上,我现在正在使用一个类(我认为我是 OO)所以我想用一个类方法来做它,但一个函数可能更好。就像是:
def wrapped_run(**kargs):
def run(**key_args):
print key_args
return wrap-the-run-function(run, kargs)
run = wrapped_run(ans=42)
run.func_code.co_argcount # => 1
run.func_code.co_varnames # => ('ans','key_args') ## keep the 'key_args' or not
run.func_defaults # => (42,)
run() # print {'ans':42} …Run Code Online (Sandbox Code Playgroud) python ×10
methods ×5
oop ×3
function ×2
cython ×1
dynamic ×1
partials ×1
python-3.x ×1
python-mock ×1
self ×1