Python 的 getattr/setattr 与 pybind11 中的 attr 相关函数
Python 内建函数
getattr
getattr 是 Python 的内建函数。它用于获取对象指定名称的属性值。
函数签名:
getattr(object, name)
getattr(object, name, default)
object: 需要从中获取属性的对象。name: 属性的名称,必须是字符串。default: (可选) 如果属性不存在时返回的默认值。
如果 name 指定的属性存在,则返回该属性的值。例如,getattr(x, 'foobar') 等价于 x.foobar。
如果属性不存在:
- 如果提供了
default参数,则返回default。 - 否则,会抛出
AttributeError。
注意:属性名 name 不一定需要是合法的 Python 标识符。但是,私有属性(名称前有两个下划线)需要手动进行名称 mangling 才能通过 getattr 访问。
setattr
setattr 是 Python 的内建函数,与 getattr 相对应。它用于设置对象的属性值。
函数签名:
setattr(object, name, value)
object: 需要设置属性的对象。name: 属性的名称,必须是字符串。value: 要赋给属性的值。
name 可以是现有属性的名称,也可以是新属性的名称。只要对象允许,函数就会将 value 赋给该属性。例如,setattr(x, 'foobar', 123) 等价于 x.foobar = 123。
属性名 name 同样不一定需要是 Python 标识符。如果属性名不是标识符,则无法使用点运算符(.)访问,但仍可通过 getattr 等函数访问。
注意:与 getattr 类似,设置私有属性也需要手动进行名称 mangling。
测试示例
创建一个名为 test.py 的文件:
class Animal:
legs = 4
def run(self):
print("I'm running!")
if __name__ == "__main__":
a = Animal()
print("Initial leg value:", getattr(a, "legs"))
setattr(a, "legs", 2)
print("After set to 2:", getattr(a, "legs"))
print("run method:", getattr(a, "run"))
print("Call run method:")
getattr(a, "run")()
运行结果:
Initial leg value: 4
After set to 2: 2
run method: <bound method Animal.run of <__main__.Animal object at 0x...>>
Call run method:
I'm running!
pybind11 - Python 内建函数映射
pybind11 提供了 C++ 函数来映射 Python 的内建函数,这些函数通常与 Python 的版本行为一致。
py::getattr
pybind11 提供了 py::getattr 函数来获取 C++ 中的 Python 对象属性。
函数签名示例:
object getattr(handle obj, handle name);
object getattr(handle obj, const char *name);
object getattr(handle obj, handle name, handle default_);
object getattr(handle obj, const char *name, handle default_);
py::setattr
pybind11 提供了 py::setattr 函数来设置 C++ 中的 Python 对象属性。
函数签名示例:
void setattr(handle obj, handle name, handle value);
void setattr(handle obj, const char *name, handle value);
测试示例 (pybind11 - getattr/setattr)
创建一个名为 demo.cpp 的文件:
#include <pybind11/pybind11.h>
#include <pybind11/embed.h> // py::scoped_interpreter
#include <string>
namespace py = pybind11;
int main() {
// 启动 Python 解释器
py::scoped_interpreter guard{};
// 使用 py::exec 定义 class
py::dict scope;
py::exec(R"(
class Person:
def __init__(self):
self.name = "Alice"
def greet(self):
return "Hi, " + self.name
p = Person()
)", scope);
// 从 scope 获取 Python 对象
py::object obj = scope["p"];
// 使用 py::getattr / py::setattr
py::print("Via py::getattr:", py::getattr(obj, "name"));
py::setattr(obj, "name", py::str("Charlie"));
py::print("After py::setattr:", py::getattr(obj, "name"));
py::print("Greeting:", py::getattr(obj, "greet")());
return 0;
}
编译和运行(需要根据你的 Python 版本调整链接标志):
c++ -O3 -Wall -std=c++11 demo.cpp -o demo \
$(python3 -m pybind11 --includes) \
-lpython3.8 -pthread -ldl -lutil -lm
./demo
运行结果:
Via py::getattr: Alice
After py::setattr: Charlie
Greeting: Hi, Charlie
pybind11 - py::object_api::attr
pybind11 的 py::object 类提供了一个 attr 成员函数,用于更方便地访问和修改对象的属性。
obj.attr("attribute_name") 返回一个可以进行 get/set 操作的对象访问器。
测试示例 (pybind11 - attr)
修改 demo.cpp 文件:
#include <pybind11/pybind11.h>
#include <pybind11/embed.h> // py::scoped_interpreter
#include <string>
#include <typeinfo> // For typeid
namespace py = pybind11;
int main() {
// 启动 Python 解释器
py::scoped_interpreter guard{};
// 用 py::exec 定义 class
py::dict scope;
py::exec(R"(
class Person:
def __init__(self):
self.name = "Alice"
def greet(self):
return "Hi, " + self.name
p = Person()
)", scope);
// 从 scope 取得 Python 对象
py::object obj = scope["p"];
// 取得属性
py::print("Via .attr get:", obj.attr("name"));
// 修改属性
obj.attr("name") = "Bob";
py::print("After .attr() set:", obj.attr("name"));
// 呼叫方法
py::print("Greeting:", obj.attr("greet")());
// 检视型别
// 注意:typeid().name() 返回的是 C++ 内部表示,可能不是直接的 Python 类型名
py::print("Type of greet result:", typeid(obj.attr("greet")()).name());
return 0;
}
使用相同的指令编译执行后结果如下:
Via .attr get: Alice
After .attr() set: Bob
Greeting: Hi, Bob
Type of greet result: N8pybind116objectE
输出 N8pybind116objectE 是 pybind11::object 的 C++ 内部表示。
常用 pybind11 函数说明
py::scoped_interpreter
这是一个 RAII(Resource Acquisition Is Initialization)类,用于管理 Python 解释器的生命周期。它会在构造时初始化 Python 解释器,并在析构时关闭解释器并释放资源。在 scoped_interpreter 对象超出作用域后,不能再调用任何 Python API。
py::dict
这是 pybind11 对 Python 字典(dict)的封装。可以用来表示 Python 的字典对象,并进行读写操作。
py::exec
该函数允许从 C++ 字符串中执行 Python 代码。它支持执行 Python 语句(statements)。使用 C++11 的原始字符串字面量(raw string literals)可以方便地处理包含特殊字符的 Python 代码。
py::print
为了解决 C++ 的 std::cout 和 Python 的 print 在输出顺序上可能出现的问题,pybind11 提供了 py::print() 函数。该函数会将输出写入 Python 的 sys.stdout,确保输出的一致性。它模仿了 Python 的 print 函数,并支持 sep, end, file, flush 等可选参数。
py::str
这是 pybind11 对 Python 字符串(str)的封装。它允许将 C++ 字符串转换为 Python 字符串对象,或获取 Python 对象的字符串表示形式,类似于 Python 的 str() 函数。