Python中如何实现类似C++中对函数赋值的功能?

最近在用 pybind11 绑定 c++中的一个库到 Python 中,遇到了一个问题

待绑定的类方法如下

class Ciphertext
{
public:
    inline auto &scale() noexcept
    {
        return scale_;
    }
inline auto &scale() const noexcept
{
    return scale_;
}

private:
    double scale_ = 1.0;

}

其例子中,有这样的调用:

Ciphertext x1_encrypted
x1_encrypted.scale() = pow(2.0, 40)

我是这样绑定的,可以成功访问到 scale 的值:

py::class_<Ciphertext>(m, "Ciphertext")
    .def("scale", (double &(Ciphertext::*)()) &Ciphertext::scale)

但是,显然不能这样在 python 中给它赋值,请问有什么方法?还是说,只能自己去增加 set 方法?

谢谢各位了!


Python中如何实现类似C++中对函数赋值的功能?

22 回复

C++ 这样是返回了引用,我印象中 Python 没有等价的写法吧?建议按 Python 的风格弄成 getter setter。


在Python里,你想直接像C++那样给函数赋值(比如 void (*funcPtr)(int) = someFunction;)是行不通的,因为Python的函数名本身就是引用。不过,你想达到的效果——动态地改变一个函数名指向的代码块——用Python实现起来更简单直接。

核心就一点:在Python中,函数名只是一个变量,它存储了对一个函数对象的引用。 所以,所谓的“函数赋值”,其实就是普通的变量赋值。

看个例子就全明白了:

def original_function(x):
    return x * 2

def new_function(x):
    return x ** 2

# 现在,`my_func` 这个变量指向了 `original_function`
my_func = original_function
print(my_func(5))  # 输出: 10

# 这就是你要的“函数赋值”:让 `my_func` 改为指向 `new_function`
my_func = new_function
print(my_func(5))  # 输出: 25

# 你甚至可以把它指向一个 lambda 表达式(匿名函数)
my_func = lambda x: x + 100
print(my_func(5))  # 输出: 105

如果你想模拟C++里函数指针作为参数传递的场景:

def processor(data, func):
    """接收一个函数`func`作为参数,并应用到`data`上"""
    return func(data)

def double(x):
    return x * 2

def square(x):
    return x * x

result1 = processor(10, double)   # 传递 double 函数
print(result1)  # 输出: 20

result2 = processor(10, square)   # 传递 square 函数
print(result2)  # 输出: 100

更高级点,你可以把函数放在列表或字典里,实现一个简单的“函数表”或分发器:

def op_add(a, b):
    return a + b

def op_subtract(a, b):
    return a - b

# 函数字典
operation_map = {
    'add': op_add,
    'subtract': op_subtract,
    'multiply': lambda a, b: a * b  # 直接内联lambda
}

# 使用
command = 'multiply'
func_to_call = operation_map[command]
result = func_to_call(5, 3)
print(result)  # 输出: 15

总结一下: 在Python里,直接把函数当变量用就行,赋值、传参、放进容器里,怎么都行,比C++的函数指针直观多了。

一句话建议: 在Python中,把函数当作普通对象进行赋值和传递即可实现类似功能。

#1 嗯嗯,最后我还是在 Ciphertext 类里增加了一个 set_scale 方法 ,谢谢

并不建议这么做,可以用 def_property 定义成可直接读写的属性。
https://pybind11.readthedocs.io/en/stable/classes.html#instance-and-static-fields
另外看文档也许可以试试 def_readwrite ?

#3 试过了,不可行,def_property 需要 set 和 get,c++里没有,def_readwrite 会说 scale 是私有的

最近正在用,我不想搞成 python 那种 getter setter 的方法,于是选择了函数重载,也蛮好用的,仅供参考

把 int 包成 class 然后返回引用(逃

#5 还是要改 c++代码吧?

对的,得改一下

getter setter 当然要自己写呀。

#9 嗯嗯,了解,我现在想另一个问题

#8 想请你们看下

<br>template&lt;typename T,<br> typename = std::enable_if_t&lt;std::is_same&lt;T, double&gt;::value ||<br> std::is_same&lt;T, std::complex&lt;double&gt;&gt;::value&gt;&gt;<br> inline void decode(const Plaintext &amp;plain, std::vector&lt;T&gt; &amp;destination,<br> MemoryPoolHandle pool = MemoryManager::GetPool())<br> {<br> decode_internal(plain, destination, std::move(pool));<br> }<br>
这种 template 怎么绑定
是参照这个嘛?

https://github.com/pybind/pybind11/blob/master/tests/test_opaque_types.cpp

https://github.com/pybind/pybind11/issues/1854

看上去 destination 是返回值吧?那直接返回就好了吧。

#11 我是想不改 c++代码可以吗

destination = []
func(destination) { … }
destination = […]

类似于这样

什么意思?

#13
就是我 list 变量放进函数里进行操作,按照 Python 的内存管理,显然不会对函数外的 list 产生影响

现在我想在不改变 c++代码的情况下,通过 pybind11 绑定完成 list 变量生成

#13 按文档里说的,应该是声明 std::vector<double>opaque 类型就可以了
https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html
但是,我不知道为啥,还是不行。

#13 我想我大概有办法了,我重新定义了一个 py::class_<DoubleVector>(m, “DoubleVector”),
现在想怎么把 operator[]加进去,方便 python 里直接 list[123]这样直接索引赋值

#13 我想我大概有办法了,我重新定义了一个 PYBIND11_MAKE_OPAQUE(std::vector<double>); py::class_<DoubleVector>(m, “DoubleVector”),
现在想怎么把 operator[] 加进去,方便 python 里直接 list[123]这样直接索引赋值

不好意思才看到,我没有读懂你的问题;因为 python 传参数如果是可变量也是引用的,如果你要是要传入 CPP 的函数里,我没这么操作过抱歉帮不到你,但是为什么不在 CPP 里生成好再传出来呢 :) 虽说是胶水语言,但毕竟一层干一层的事情嘛。

我用的是 boost::python,pybind 看起来像是在其上又封装了一层,绑定机制什么的看起来都一样的。如果 pybind 文档少,你可以查查 boost 的相关资料。

这个我试过,你甚至可以直接定义一个 hash 导出到类里,这样你的自定义 CPP 类就支持了 Python 的 hash 哦 :)

#19 我想知道__getitem__,hash 这样怎么写到自定义类里,请问能给个参考吗?

#19
<br>py::class_&lt;uIntVector&gt;(m, "uIntVector")<br> .def("__getitem__", [](const uIntVector &amp;v, int i) {<br> return v[i];<br> }, py::keep_alive&lt;1, 2&gt;());<br><br>
我这样写可以索引取值了,但还不能赋值,也没考虑切片的情况

回到顶部