GVKun编程网logo

猴子补丁Python类(猴子补丁 python)

3

本篇文章给大家谈谈猴子补丁Python类,以及猴子补丁python的知识点,同时本文还将给你拓展5、pytest--猴子补丁、Boost-python如何将C++类实例传递给python类、Boost

本篇文章给大家谈谈猴子补丁Python类,以及猴子补丁 python的知识点,同时本文还将给你拓展5、pytest -- 猴子补丁、Boost-python如何将C ++类实例传递给python类、Boost-python如何将c类实例传递给python类、monkey patch(猴子补丁)等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

猴子补丁Python类(猴子补丁 python)

猴子补丁Python类(猴子补丁 python)

我有一个类,位于一个单独的模块中,无法更改。

from module import MyClassclass ReplaceClass(object)  ...MyClass = ReplaceClass

除了此文件之外,这不会更改MyClass的其他任何位置。但是,如果我添加这样的方法

def bar():   print 123MyClass.foo = bar

这将起作用,并且foo方法将在其他任何地方都可用。

如何完全替换班级?

答案1

小编典典
import moduleclass ReplaceClass(object):    ....module.MyClass = ReplaceClass

5、pytest -- 猴子补丁

5、pytest -- 猴子补丁

[TOC]

有时候,测试用例需要调用某些依赖于全局配置的功能,或者这些功能本身又调用了某些不容易测试的代码(例如:网络接入)。fixture monkeypatch 可以帮助你安全的设置 / 删除一个属性、字典项或者环境变量,甚至改变导入模块时的 sys.path 路径。

monkeypatch 提供了以下方法:

monkeypatch.setattr(obj, name, value, raising=True)
monkeypatch.delattr(obj, name, raising=True)
monkeypatch.setitem(mapping, name, value)
monkeypatch.delitem(obj, name, raising=True)
monkeypatch.setenv(name, value, prepend=False)
monkeypatch.delenv(name, raising=True)
monkeypatch.syspath_prepend(path)
monkeypatch.chdir(path)

所有的修改将在测试用例或者 fixture 执行完成后撤销。raising 参数表明:当设置 / 删除操作的目标不存在时,是否上报 KeyErrorAttributeError 异常。

1. 修改函数功能或者类属性

使用 monkeypatch.setattr() 可以将函数或者属性修改为你希望的行为,使用 monkeypatch.delattr() 可以删除测试用例使用的函数或者属性;

参考以下三个例子:

  • 在这个例子中,使用 monkeypatch.setattr() 修改 Path.home 方法,在测试运行期间,它一直返回的是固定的 Path("/abc"),这样就移除了它在不同平台上的依赖;测试运行完成后,对 Path.home 的修改会被撤销;

    # src/chapter-5/test_module.py
    
    from pathlib import Path
    
    
    def getssh():
        return Path.home() / ".ssh"
    
    
    def test_getssh(monkeypatch):
        def mockreturn():
            return Path("/abc")
    
        # 替换 Path.home
        # 需要在真正的调用之前执行
        monkeypatch.setattr(Path, "home", mockreturn)
    
        # 将会使用 mockreturn 代替 Path.home
        x = getssh()
        assert x == Path("/abc/.ssh")
    
  • 在这个例子中,使用 monkeypatch.setattr() 结合类,模拟函数的返回对象;

    假设我们有一个简单的功能,访问一个 url 返回网页内容:

    # src/chapter-5/app.py
    
    from urllib import request
    
    
    def get(url):
        r = request.urlopen(url)
        return r.read().decode(''utf-8'')
    

    我们现在要去模拟 r,它需要一个.read() 方法返回的是 bytes 的数据类型;我们可以在测试模块中定义一个类来代替 r

    # src/chapter-5/test_app.py
    
    from urllib import request
    
    from app import get
    
    
    # 自定义的类模拟 urlopen 的返回值
    class MockResponse:
    
        # 永远返回一个固定的 bytes 类型的数据
        @staticmethod
        def read():
            return b''luizyao.com''
    
    
    def test_get(monkeypatch):
        def mock_urlopen(*args, **kwargs):
            return MockResponse()
    
        # 使用 request.mock_urlopen 代替 request.urlopen
        monkeypatch.setattr(request, ''urlopen'', mock_urlopen)
    
        data = get(''https://luizyao.com'')
        assert data == ''luizyao.com''
    

    你可以继续为实际的场景构建更具有复杂度的 MockResponse;例如,你可以包含一个总是返回 Trueok 属性,或者根据输入的字符串为 read() 返回不同的值;

    我们也可以通过 fixture 跨用例共享:

    # src/chapter-5/test_app.py
    
    import pytest
    
    
    # monkeypatch 是 function 级别作用域的,所以 mock_response 也只能是 function 级别,
    # 否则会报 ScopeMismatch 
    @pytest.fixture
    def mock_response(monkeypatch):
        def mock_urlopen(*args, **kwargs):
            return MockResponse()
    
        # 使用 request.mock_urlopen 代替 request.urlopen
        monkeypatch.setattr(request, ''urlopen'', mock_urlopen)
    
    
    # 使用 mock_response 代替原先的 monkeypatch
    def test_get_fixture1(mock_response):
        data = get(''https://luizyao.com'')
        assert data == ''luizyao.com''
    
    
    # 使用 mock_response 代替原先的 monkeypatch
    def test_get_fixture2(mock_response):
        data = get(''https://bing.com'')
        assert data == ''luizyao.com''
    

    注意:

    • 测试用例使用的 fixture 由原先的 mock_response 替换为 monkeypatch
    • 因为 monkeypatchfunction 级别作用域的,所以 mock_response 也只能是 function 级别,否则会报 ScopeMismatch: You tried to access the ''function'' scoped fixture ''monkeypatch'' with a ''module'' scoped request object 错误;
    • 如果你想让 mock_response 应用于所有的测试用例,可以考虑将它移到 conftest.py 里面,并标记 autouse=True
  • 在这个例子中,使用 monkeypatch.delattr() 删除 urllib.request.urlopen() 方法;

    # src/chapter-5/test_app.py
    
    @pytest.fixture
    def no_request(monkeypatch):
        monkeypatch.delattr(''urllib.request.urlopen'')
    
    
    def test_delattr(no_request):
        data = get(''https://bing.com'')
        assert data == ''luizyao.com''
    

    执行:

    λ pipenv run pytest --tb=native --assert=plain --capture=no src/chapter-5/test_app.
    py::test_delattr
    =============================== test session starts ================================ 
    platform win32 -- Python 3.7.3, pytest-5.1.3, py-1.8.0, pluggy-0.13.0
    rootdir: D:\Personal Files\Projects\pytest-chinese-doc
    collected 1 item
    
    src\chapter-5\test_app.py F
    
    ===================================== FAILURES ===================================== 
    ___________________________________ test_delattr ___________________________________ 
    Traceback (most recent call last):
      File "D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-5\test_app.py", line 78, in test_delattr
        data = get(''https://bing.com'')
      File "D:\Personal Files\Projects\pytest-chinese-doc\src\chapter-5\app.py", line 26, in get
        r = request.urlopen(url)
    AttributeError: module ''urllib.request'' has no attribute ''urlopen''
    ================================ 1 failed in 0.04s =================================
    

    注意:

    • 避免删除内置库中的方法,如果一定要这么做,最好加上 --tb=native --assert=plain --capture=no

    • 修改 pytest 使用到的库,可能会污染 pytest 本身,建议使用 MonkeyPatch.context(),它返回一个 MonkeyPatch 对象,结合 with 限制这些修改只发生在包裹的代码中。

      def test_stdlib(monkeypatch):
      with monkeypatch.context() as m:
          m.setattr(functools, "partial", 3)
          assert functools.partial == 3
      

2. 修改环境变量

使用 monkeypatchsetenv()delenv() 方法,可以在测试中安全的设置 / 删除环境变量;

# src/chapter-5/test_env.py

import os

import pytest


def get_os_user():
    username = os.getenv(''USER'')

    if username is None:
        raise IOError(''"USER" environment variable is not set.'')

    return username


def test_user(monkeypatch):
    monkeypatch.setenv(''USER'', ''luizyao'')
    assert get_os_user() == ''luizyao''


def test_raise_exception(monkeypatch):
    monkeypatch.delenv(''USER'', raising=False)
    pytest.raises(IOError, get_os_user)

monkeypatch.delenv()raising 要设置为 False,否则可能会报 KeyError

你也可以使用 fixture,实现跨用例共享:

import pytest


@pytest.fixture
def mock_env_user(monkeypatch):
    monkeypatch.setenv("USER", "TestingUser")


@pytest.fixture
def mock_env_missing(monkeypatch):
    monkeypatch.delenv("USER", raising=False)


# notice the tests reference the fixtures for mocks
def test_upper_to_lower(mock_env_user):
    assert get_os_user_lower() == "testinguser"


def test_raise_exception(mock_env_missing):
    with pytest.raises(OSError):
        _ = get_os_user_lower()

3. 修改字典

使用 monkeypatch.setitem() 方法可以在测试期间安全的修改字典中特定的值;

DEFAULT_CONFIG = {"user": "user1", "database": "db1"}


def create_connection_string(config=None):
    config = config or DEFAULT_CONFIG
    return f"User Id={config[''user'']}; Location={config[''database'']};"

我们可以修改数据库的用户或者使用其它的数据库:

import app


def test_connection(monkeypatch):
    monkeypatch.setitem(app.DEFAULT_CONFIG, "user", "test_user")
    monkeypatch.setitem(app.DEFAULT_CONFIG, "database", "test_db")

    expected = "User Id=test_user; Location=test_db;"

    result = app.create_connection_string()
    assert result == expected

可以使用 monkeypatch.delitem 删除指定的项:

import pytest

import app


def test_missing_user(monkeypatch):
    monkeypatch.delitem(app.DEFAULT_CONFIG, "user", raising=False)

    with pytest.raises(KeyError):
        _ = app.create_connection_string()

GitHub 仓库地址:https://github.com/luizyao/pytest-chinese-doc

Boost-python如何将C ++类实例传递给python类

Boost-python如何将C ++类实例传递给python类

我是新来提升python的人。我必须先在cpp代码中初始化一个cpp类实例,然后将此cpp实例传递给python代码,然后使用python类实例来调用它(cpp实例)。我已经尝试了Python
/ C API的方式,但是失败了,所以我想知道如何将c ++类实例传递给python类。

以下是我的代码,从boost python演示更改了。

在main.cpp中

#include <python2.6/Python.h>
#include <boost/python.hpp>
#include <iostream>

using namespace boost::python;
using namespace std;

class World
{
private:
    string name;
public:
    void set(string name)
    {
        this->name = name;
    }
    void greet()
    {
        cout << "hello,I am " << name << endl;
    }
};

typedef boost::shared_ptr< World > world_ptr;

BOOST_PYTHON_MODULE(hello)
{
    class_<World>("World")
    .def("greet",&World::greet)
    .def("set",&World::set)
    ;

    register_ptr_to_python<world_ptr>();
};

int main()
{
    Py_Initialize();
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");

    world_ptr worldObjectPtr (new World);
    worldObjectPtr->set("C++!");

    try
    {
        inithello();
        PyObject* pModule =PyImport_ImportModule("python");
        PyObject* pDict = PyModule_GetDict(pModule);
        PyObject* pClassHelloPython = PyDict_GetItemString(pDict,"Person");
        PyObject* pInstanceHelloPython = PyInstance_New(pClassHelloPython,NULL,NULL);

        PyObject_CallMethod(pInstanceHelloPython,"sayHi",NULL);
        worldObjectPtr->greet();
        PyObject_CallMethod(pInstanceHelloPython,"greetReset","O",worldObjectPtr);
        worldObjectPtr->greet();
    }
    catch (error_already_set)
    {
        PyErr_Print();
    }

    Py_Finalize();

    return 0;
}

在python.py中

class Person:
    def sayHi(self):
        print 'hello from python'

    def greetReset(self,instance):
        instance.set('Python')

在上面的代码中,我想将worldObjectPtr传递给pInstanceHelloPython,因此,pInstanceHelloPython可以将worldObjectPtr->
name设置为Python。但是我只是不知道该怎么做。谢谢您的耐心配合!!

Boost-python如何将c类实例传递给python类

Boost-python如何将c类实例传递给python类

我是新增的 python.我必须首先在cpp代码中初始化一个cpp类实例,然后将此cpp实例传递给python代码,使用python类实例来调用它(cpp实例).我已经尝试过 Python / C API的方式,但失败了,所以我不知道如何传递一个c类的实例到一个python类.

以下是我的代码,从boost python演示中更改.

在main.cpp

#include <python2.6/Python.h>
#include <boost/python.hpp>
#include <iostream>

using namespace boost::python;
using namespace std;

class World
{
private:
    string name;
public:
    void set(string name)
    {
        this->name = name;
    }
    void greet()
    {
        cout << "hello,I am " << name << endl;
    }
};

typedef boost::shared_ptr< World > world_ptr;

BOOST_PYTHON_MODULE(hello)
{
    class_<World>("World")
    .def("greet",&World::greet)
    .def("set",&World::set)
    ;

    register_ptr_to_python<world_ptr>();
};

int main()
{
    Py_Initialize();
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('./')");

    world_ptr worldobjectPtr (new World);
    worldobjectPtr->set("C++!");

    try
    {
        inithello();
        PyObject* pModule =PyImport_ImportModule("python");
        PyObject* pDict = PyModule_GetDict(pModule);
        PyObject* pClassHelloPython = PyDict_GetItemString(pDict,"Person");
        PyObject* pInstanceHelloPython = PyInstance_New(pClassHelloPython,NULL,NULL);

        PyObject_CallMethod(pInstanceHelloPython,"sayHi",NULL);
        worldobjectPtr->greet();
        PyObject_CallMethod(pInstanceHelloPython,"greetReset","O",worldobjectPtr);
        worldobjectPtr->greet();
    }
    catch (error_already_set)
    {
        PyErr_Print();
    }

    Py_Finalize();

    return 0;
}

在python.py

class Person:
    def sayHi(self):
        print 'hello from python'

    def greetReset(self,instance):
        instance.set('Python')

在上面的代码中,我想将worldobjectPtr传递给pInstanceHelloPython,因此,pInstanceHelloPython可以将worldobjectPtr->名称设置为Python.但我只是不知道该怎么做.谢谢你的耐心提前!

解决方法

通过boost :: python :: ptr将对象指针传递给python.这将阻止python解释器复制:
#include <boost/python.hpp>
#include <string>
#include <iostream>

using namespace boost::python;
using namespace std;

class World
{
private:
    string name;
public:
    void set(string name) {
        this->name = name;
    }
    void greet() {
        cout << "hello,I am " << name << endl;
    }
};

typedef boost::shared_ptr< World > world_ptr;

BOOST_PYTHON_MODULE(hello)
{
    class_<World>("World")
        .def("greet",&World::greet)
        .def("set",&World::set)
    ;
};

int main(int argc,char **argv)
{
    Py_Initialize();
    try {
        PyRun_SimpleString(
            "class Person:\n"
            "    def sayHi(self):\n"
            "        print 'hello from python'\n"
            "    def greetReset(self,instance):\n"
            "        instance.set('Python')\n"
          );

        world_ptr worldobjectPtr (new World);
        worldobjectPtr->set("C++!");

        inithello();
        object o_main 
            = object(handle<>(borrowed(PyImport_AddModule("__main__"))));
        object o_person_type = o_main.attr("Person");
        object o_person = o_person_type();
        object o_func1 = o_person.attr("sayHi");
        o_func1();
        object o_func2 = o_person.attr("greetReset");
        o_func2(boost::python::ptr(worldobjectPtr.get()));
        worldobjectPtr->greet();
    }
    catch (error_already_set) {
        PyErr_Print();
    }

    Py_Finalize();

    return 0;
}

monkey patch(猴子补丁)

monkey patch(猴子补丁)

前言

本次依然是选自python面试题系列,将一个比较偏的概念,可能很多人没怎么听说过——猴子补丁,其实所讲的内容很简单,它得益于python灵活的语法、一切皆对象的思想,一起来看看看看吧!

目录

一、什么是monkey patch

二、monkey patch的功能简介

2.1 运行时动态改变类的方法

2.2 monkey patch的应用场景
一、什么是monkey patch

为什么叫猴子补丁?这其实是一个很难回答的问题,似乎和Python语言没啥关系,而且也和它所实现的功能扯不上什么关系,但是偏偏就这么叫了,那就姑且这么称呼吧。

关于猴子补丁的由来网上查到两种说法:

1,这个词原来为Guerrilla Patch,杂牌军、游击队,说明这部分不是原装的,在英文里guerilla发音和gorllia(猩猩)相似,再后来就写了monkey(猴子)。 2,还有一种解释是说由于这种方式将原来的代码弄乱了(messing with it),在英文里叫monkeying about(顽皮的),所以叫做Monkey Patch。

外国人有时候总是会给一些概念莫名其妙的称呼,这就不管他了,关键是理解它的本质和功能才是最重要的。

先明确一个观点:猴子补丁(monkey patch)的主要功能就是动态的属性的替换。虽然属性的运行时替换和猴子也没什么关系,所以说猴子补丁的叫法有些莫名其妙,但是只要和“模块运行时替换的功能”对应就行了。

二、monkey patch的功能简介

monkey patch允许在运行期间动态修改一个类或模块(注意python中一切皆对象,包括类、方法、甚至是模块)

2.1 运行时动态改变类的方法

先看一个简单的例子:

class A:

def func(self):
    print("Hi")
def monkey(self):
    print("Hi, monkey")

a = A()
a.func()
''''''运行结果
Hi
''''''

上面的结果无可厚非,大家都知道,但是看一下下面的例子:

class A:

def func(self):
    print("Hi")
def monkey(self):
    print("Hi, monkey")

a = A()
A.func=A.monkey #在运行的时候,才改变了func
a.func()
''''''运行结果
Hi, monkey
''''''

就这么简单,其实这根本的原因在于Python语法的灵活性,方法可以像普通对象那样使用。

我们还可以这样做:

class A:

def func(self):
    print("Hi")
def monkey(self):
    print("Hi, monkey")

def outer_monkey(a): # a 这个参数是没有用到的,因为func有一个参数,如果这个函数没有参数的话不能这样直接赋值

print("Hi,outer monkey")

a = A()
A.func=outer_monkey
a.func()
''''''运行结果
Hi, outer monkey
''''''

将类外面的普通方法依然可以在程序运行的时候动态赋值给类的某一个方法。

总结:上面所展示的其实就是monkey patch,即运行时动态改变方法、类的方法。其实不管是定义在类外的普通方法、类里面的方法、甚至是模块这些都可以进行“动态替换的操作”,不得不感叹python真的是一门简洁灵活的语言。

2.2 monkey patch的应用场景

这里有一个比较实用的例子,很多代码用到 import json,后来发现ujson性能更高,如果觉得把每个文件的import json 改成 import ujson as json成本较高,或者说想测试一下用ujson替换json是否符合预期,只需要在入口加上:

import json
import ujson

def monkey_patch_json():

json.\_\_name\_\_ \= ''ujson''  
json.dumps \= ujson.dumps  
json.loads \= ujson.loads  

monkey_patch_json()

其实这种场景也比较多,比如我们引用团队通用库里的一个模块,又想丰富模块的功能,除了继承之外也可以考虑用Monkey Patch。个人感觉Monkey Patch带了便利的同时也有搞乱源代码优雅的风险。

关于猴子补丁Python类猴子补丁 python的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于5、pytest -- 猴子补丁、Boost-python如何将C ++类实例传递给python类、Boost-python如何将c类实例传递给python类、monkey patch(猴子补丁)的相关知识,请在本站寻找。

本文标签: