Skip to content

第5章 类与对象绑定

类绑定是 pybind11 最强大的特性之一。通过 py::class_ 可以将 C++ 类完整地暴露给 Python,包括构造函数、成员函数、静态函数、属性、运算符重载和继承层次。

class_basic.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 定义一个简单的 C++ 类
class Point {
public:
Point(int x, int y) : m_x(x), m_y(y) {}
int x() const { return m_x; }
int y() const { return m_y; }
void set_x(int x) { m_x = x; }
void set_y(int y) { m_y = y; }
private:
int m_x;
int m_y;
};
PYBIND11_MODULE(class_basic, m) {
// 使用 py::class_<T> 绑定类
// 语法: py::class_<ClassName>(m, "PythonName")
py::class_<Point>(m, "Point")
// .def(py::init<...>()) 绑定构造函数
.def(py::init<int, int>(),
py::arg("x") = 0,
py::arg("y") = 0,
"构造函数:创建一个 Point 对象")
// 绑定成员函数
.def("x", &Point::x, "获取 x 坐标")
.def("y", &Point::y, "获取 y 坐标")
.def("set_x", &Point::set_x, py::arg("x"), "设置 x 坐标")
.def("set_y", &Point::set_y, py::arg("y"), "设置 y 坐标")
// 绑定 __repr__
.def("__repr__", [](const Point& p) {
return py::str("<Point({}, {})>").format(p.x(), p.y());
});
}
import class_basic as cb
p = cb.Point(3, 4)
print(p) # <Point(3, 4)>
print(p.x()) # 3
print(p.y()) # 4
p.set_x(10)
print(p.x()) # 10
py::class_<Point>(m, "Point")
.def(py::init<int, int>(), ...)
表达式含义
py::class_<Point>模板参数是 C++ 类类型
(m, "Point")在模块 m 中注册为 "Point"
py::init<int, int>()绑定构造函数,参数类型为 int, int
&Point::x成员函数指针
constructor_overload.cpp
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
class Rectangle {
public:
Rectangle(int width, int height) : m_w(width), m_h(height) {}
Rectangle() : m_w(0), m_h(0) {}
int area() const { return m_w * m_h; }
int perimeter() const { return 2 * (m_w + m_h); }
private:
int m_w;
int m_h;
};
PYBIND11_MODULE(constructor_overload, m) {
py::class_<Rectangle>(m, "Rectangle")
// 默认构造函数
.def(py::init<>(), "创建零面积矩形")
// 带参数的构造函数
.def(py::init<int, int>(),
py::arg("width") = 1,
py::arg("height") = 1,
"创建指定宽高的矩形")
.def("area", &Rectangle::area, "计算面积")
.def("perimeter", &Rectangle::perimeter, "计算周长");
}
import constructor_overload as co
r1 = co.Rectangle() # 默认构造
r2 = co.Rectangle(3, 4) # 带参数构造
r3 = co.Rectangle(width=5) # 部分参数
print(r2.area()) # 12
print(r3.perimeter()) # 12 (5+5+1+1)
member_functions.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Circle {
public:
Circle(double radius) : m_radius(radius) {}
// getter
double radius() const { return m_radius; }
// setter
void set_radius(double r) {
if (r < 0) throw std::runtime_error("Radius cannot be negative");
m_radius = r;
}
// 计算方法
double area() const { return 3.14159 * m_radius * m_radius; }
double circumference() const { return 2 * 3.14159 * m_radius; }
private:
double m_radius;
};
PYBIND11_MODULE(member_functions, m) {
py::class_<Circle>(m, "Circle")
.def(py::init<double>(), py::arg("radius") = 1.0, "创建圆")
.def("radius", &Circle::radius, "获取半径")
.def("set_radius", &Circle::set_radius, py::arg("radius"), "设置半径")
.def("area", &Circle::area, "计算面积")
.def("circumference", &Circle::circumference, "计算周长")
.def("__repr__", [](const Circle& c) {
return py::str("<Circle(radius={:.2f})>").format(c.radius());
});
}
import member_functions as mf
c = mf.Circle(5.0)
print(c.radius()) # 5.0
print(c.area()) # 78.53975
print(c.circumference()) # 31.4159
c.set_radius(3.0)
print(c.area()) # 28.27431
try:
c.set_radius(-1.0)
except RuntimeError as e:
print(f"Error: {e}") # Radius cannot be negative
static_methods.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class MathUtils {
public:
// 静态成员函数:不需要实例即可调用
static int add(int a, int b) { return a + b; }
static int multiply(int a, int b) { return a * b; }
// 静态成员变量
static constexpr double PI = 3.14159265358979;
// 非静态成员函数(普通方法)
int power(int base, int exp) const {
int result = 1;
for (int i = 0; i < exp; ++i) result *= base;
return result;
}
};
PYBIND11_MODULE(static_methods, m) {
py::class_<MathUtils>(m, "MathUtils")
.def(py::init<>(), "创建 MathUtils 实例")
// 静态函数绑定使用 .def_static()
.def_static("add", &MathUtils::add, "两数相加")
.def_static("multiply", &MathUtils::multiply, "两数相乘")
// 暴露静态常量
.attr("PI") = MathUtils::PI;
// 直接在模块级别暴露静态函数(不需要类)
m.def("math_add", &MathUtils::add, "使用 MathUtils.add 的顶层函数");
}
import static_methods as sm
print(sm.MathUtils.add(3, 4)) # 7
print(sm.MathUtils.multiply(3, 4)) # 12
print(sm.MathUtils.PI) # 3.14159265358979
math = sm.MathUtils()
print(math.power(2, 10)) # 1024
.class_<MyClass>(m, "MyClass")
.def("instance_method", &MyClass::instance_method) // 实例方法
.def_static("static_method", &MyClass::static_method) // 静态方法
properties.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Person {
public:
Person(const std::string& name, int age) : m_name(name), m_age(age) {}
const std::string& name() const { return m_name; }
void set_name(const std::string& n) { m_name = n; }
int age() const { return m_age; }
void set_age(int a) {
if (a < 0) throw std::runtime_error("Age cannot be negative");
m_age = a;
}
private:
std::string m_name;
int m_age;
};
PYBIND11_MODULE(properties, m) {
py::class_<Person>(m, "Person")
.def(py::init<std::string, int>(),
py::arg("name"),
py::arg("age") = 0,
"创建 Person 对象")
// 使用 .def_property() 绑定 getter/setter
// .def_property(name, getter, setter, doc)
.def_property("name",
&Person::name,
&Person::set_name,
"姓名属性")
.def_property("age",
&Person::age,
&Person::set_age,
"年龄属性")
// 只读属性:只提供 getter
.def_property_readonly("info", [](const Person& p) {
return py::str("{} (age {})").format(p.name(), p.age());
}, "只读信息字符串");
}
import properties as p
person = p.Person("Alice", 30)
print(person.name) # Alice
print(person.age) # 30
person.name = "Bob"
person.age = 25
print(person.name) # Bob
print(person.info) # Bob (age 25)
方法用途
.def_property()可读写属性,提供 getter 和 setter
.def_property_readonly()只读属性,只有 getter
.def_property_readwrite()可读写属性,与 .def_property() 相同
readonly_property.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Database {
public:
Database(const std::string& name) : m_name(name), m_id(next_id++) {}
const std::string& name() const { return m_name; }
int id() const { return m_id; }
void set_name(const std::string& n) { m_name = n; }
private:
std::string m_name;
int m_id;
static int next_id;
};
int Database::next_id = 1;
PYBIND11_MODULE(readonly_property, m) {
py::class_<Database>(m, "Database")
.def(py::init<std::string>(), py::arg("name"), "创建数据库连接")
// name 可读写
.def_property("name", &Database::name, &Database::set_name)
// id 只读(没有 setter)
.def_property_readonly("id", &Database::id, "数据库 ID(只读)");
}
import readonly_property as rp
db = rp.Database("mydb")
print(db.name) # mydb
print(db.id) # 1
db.name = "newdb"
print(db.name) # newdb
operators.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Vector2 {
public:
Vector2(double x, double y) : m_x(x), m_y(y) {}
// 运算符重载
Vector2 operator+(const Vector2& other) const {
return Vector2(m_x + other.m_x, m_y + other.m_y);
}
Vector2 operator-(const Vector2& other) const {
return Vector2(m_x - other.m_x, m_y - other.m_y);
}
Vector2 operator*(double scalar) const {
return Vector2(m_x * scalar, m_y * scalar);
}
bool operator==(const Vector2& other) const {
return std::abs(m_x - other.m_x) < 1e-9 && std::abs(m_y - other.m_y) < 1e-9;
}
double x() const { return m_x; }
double y() const { return m_y; }
private:
double m_x;
double m_y;
};
PYBIND11_MODULE(operators, m) {
py::class_<Vector2>(m, "Vector2")
.def(py::init<double, double>(), py::arg("x") = 0, py::arg("y") = 0)
.def("x", &Vector2::x)
.def("y", &Vector2::y)
// 数学运算
.def(py::self + py::self) // __add__
.def(py::self - py::self) // __sub__
.def(py::self * double()) // __mul__ (scalar * vector)
// 比较运算
.def(py::self == py::self) // __eq__
// 支持反序运算(右操作数)
.def(double() * py::self) // __rmul__ (vector * scalar)
.def("__repr__", [](const Vector2& v) {
return py::str("Vector2({:.2f}, {:.2f})").format(v.x(), v.y());
});
}
import operators as op
v1 = op.Vector2(1.0, 2.0)
v2 = op.Vector2(3.0, 4.0)
print(v1 + v2) # Vector2(4.00, 6.00)
print(v1 - v2) # Vector2(-2.00, -2.00)
print(v1 * 2.0) # Vector2(2.00, 4.00)
print(3.0 * v1) # Vector2(3.00, 6.00) (__rmul__)
v3 = op.Vector2(1.0, 2.0)
print(v1 == v3) # True
print(v1 == v2) # False
C++ 运算符pybind11 表达式Python 魔术方法
a + bpy::self + py::self__add__
a - bpy::self - py::self__sub__
a * bpy::self * py::self__mul__
a == bpy::self == py::self__eq__
a[i]py::self[i]__getitem__
a() py::self()__call__
friend_functions.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class Point;
class PointPrinter {
public:
// 友元函数
static void print(const Point& p);
};
class Point {
public:
Point(int x, int y) : m_x(x), m_y(y) {}
int x() const { return m_x; }
int y() const { return m_y; }
// 友元声明
friend class PointPrinter;
private:
int m_x;
int m_y;
};
void PointPrinter::print(const Point& p) {
// 可以访问 Point 的私有成员
py::print(py::str("Point({}, {})").format(p.m_x, p.m_y));
}
PYBIND11_MODULE(friend_functions, m) {
py::class_<Point>(m, "Point")
.def(py::init<int, int>(), py::arg("x"), py::arg("y"))
.def("x", &Point::x)
.def("y", &Point::y);
// 绑定友元函数到模块
m.def("print_point", &PointPrinter::print, py::arg("point"), "打印 Point 对象");
}
import friend_functions as ff
p = ff.Point(3, 4)
ff.print_point(p) # 输出: Point(3, 4)
inheritance.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 基类
class Animal {
public:
virtual ~Animal() = default;
virtual std::string speak() const = 0;
virtual std::string name() const { return m_name; }
protected:
std::string m_name;
};
// 派生类
class Dog : public Animal {
public:
Dog(const std::string& name) { m_name = name; }
std::string speak() const override {
return m_name + " says: Woof!";
}
};
class Cat : public Animal {
public:
Cat(const std::string& name) { m_name = name; }
std::string speak() const override {
return m_name + " says: Meow!";
}
};
PYBIND11_MODULE(inheritance, m) {
// 基类绑定(抽象类,不能直接实例化)
py::class_<Animal>(m, "Animal")
.def("name", &Animal::name)
.def("speak", &Animal::speak, "动物发出的声音");
// 派生类绑定
py::class_<Dog, Animal>(m, "Dog")
.def(py::init<const std::string&>(), py::arg("name"))
.def("speak", &Dog::speak, "狗叫声");
py::class_<Cat, Animal>(m, "Cat")
.def(py::init<const std::string&>(), py::arg("name"))
.def("speak", &Cat::speak, "猫叫声");
}
import inheritance as inh
dog = inh.Dog("Buddy")
print(dog.name()) # Buddy
print(dog.speak()) # Buddy says: Woof!
cat = inh.Cat("Whiskers")
print(cat.name()) # Whiskers
print(cat.speak()) # Whiskers says: Meow!
animals = [dog, cat]
for a in animals:
print(f"{a.name()}: {a.speak()}")

5.10 虚函数覆写(Trampoline 模式)

Section titled “5.10 虚函数覆写(Trampoline 模式)”

当 Python 代码需要覆写 C++ 的虚函数时,需要使用 Trampoline(蹦床)模式。

trampoline.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class PyAnimal; // 前向声明
// 基类
class Animal {
public:
virtual ~Animal() = default;
// 纯虚函数
virtual std::string speak() const = 0;
// 非虚函数
virtual std::string name() const { return "Animal"; }
// 普通方法
std::string identify() const {
return "I am a " + name();
}
};
// Trampoline 类:继承自 C++ 基类,覆写虚函数以调用 Python
class PyAnimal : public Animal {
public:
// 覆写 speak
std::string speak() const override {
// 调用 Python 端的实现
PYBIND11_OVERRIDE_PURE(
std::string, // 返回类型
Animal, // 基类类型
speak, // 函数名
);
}
// 覆写 name
std::string name() const override {
PYBIND11_OVERRIDE(
std::string,
Animal,
name,
);
}
};
class Dog : public Animal {
public:
Dog(const std::string& name) : m_name(name) {}
std::string speak() const override {
return m_name + " says: Woof!";
}
std::string name() const override {
return m_name;
}
private:
std::string m_name;
};
PYBIND11_MODULE(trampoline, m) {
// 基类绑定时指定 trampoline 类
py::class_<Animal, PyAnimal>(m, "Animal")
.def(py::init<>())
.def("name", &Animal::name)
.def("speak", &Animal::speak)
.def("identify", &Animal::identify);
py::class_<Dog, Animal>(m, "Dog")
.def(py::init<const std::string&>(), py::arg("name"));
}
import trampoline as tr
dog = tr.Dog("Rex")
print(dog.speak()) # Rex says: Woof!
print(dog.identify()) # I am a Rex
class MyAnimal(tr.Animal):
def name(self):
return "CustomAnimal"
def speak(self):
return "Custom sound!"
my_animal = MyAnimal()
print(my_animal.name()) # CustomAnimal
print(my_animal.speak()) # Custom sound
print(my_animal.identify()) # I am a CustomAnimal

PYBIND11_OVERRIDE vs PYBIND11_OVERRIDE_PURE

Section titled “PYBIND11_OVERRIDE vs PYBIND11_OVERRIDE_PURE”
宏用途场景
PYBIND11_OVERRIDE_PURE基类函数是纯虚(= 0)必须覆写
PYBIND11_OVERRIDE基类函数有默认实现可选覆写
// 语法
PYBIND11_OVERRIDE_PURE(ReturnType, BaseClass, func_name)
PYBIND11_OVERRIDE(ReturnType, BaseClass, func_name, args...)
abstract.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
// 抽象基类(接口)
class IProcessor {
public:
virtual ~IProcessor() = default;
virtual std::string process(const std::string& input) = 0;
virtual int priority() const { return 0; }
};
// 具体实现
class UpperCaseProcessor : public IProcessor {
public:
std::string process(const std::string& input) override {
std::string result = input;
std::transform(result.begin(), result.end(), result.begin(), ::toupper);
return result;
}
};
class LowerCaseProcessor : public IProcessor {
public:
std::string process(const std::string& input) override {
std::string result = input;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
}
int priority() const override { return 1; }
};
// Trampoline
class PyProcessor : public IProcessor {
public:
std::string process(const std::string& input) override {
PYBIND11_OVERRIDE_PURE(
std::string,
IProcessor,
process,
input
);
}
int priority() const override {
PYBIND11_OVERRIDE(
int,
IProcessor,
priority,
);
}
};
PYBIND11_MODULE(abstract, m) {
// 绑定接口(抽象类)
py::class_<IProcessor, PyProcessor>(m, "IProcessor")
.def(py::init<>())
.def("process", &IProcessor::process, py::arg("input"), "处理输入")
.def("priority", &IProcessor::priority, "处理优先级");
// 绑定具体实现
py::class_<UpperCaseProcessor, IProcessor>(m, "UpperCaseProcessor")
.def(py::init<>());
py::class_<LowerCaseProcessor, IProcessor>(m, "LowerCaseProcessor")
.def(py::init<>());
}
import abstract as ab
upper = ab.UpperCaseProcessor()
print(upper.process("hello")) # HELLO
lower = ab.LowerCaseProcessor()
print(lower.process("WORLD")) # world
class MyProcessor(ab.IProcessor):
def process(self, input):
return f"***{input}***"
def priority(self):
return 99
my_proc = MyProcessor()
print(my_proc.process("test")) # ***test***
print(my_proc.priority()) # 99

Python 不能直接访问 C++ 的 private 成员,但可以通过公共接口间接访问。

private_members.cpp
#include <pybind11/pybind11.h>
namespace py = pybind11;
class BankAccount {
public:
BankAccount(const std::string& name, double balance)
: m_name(name), m_balance(balance) {}
// 公共接口
const std::string& name() const { return m_name; }
double balance() const { return m_balance; }
// 存款(修改私有成员的唯一方式)
void deposit(double amount) {
if (amount <= 0) throw std::runtime_error("Amount must be positive");
m_balance += amount;
}
// 取款
void withdraw(double amount) {
if (amount <= 0) throw std::runtime_error("Amount must be positive");
if (amount > m_balance) throw std::runtime_error("Insufficient funds");
m_balance -= amount;
}
// 私有成员不能直接绑定
// py::class_<BankAccount>(m, "BankAccount")
// .def_readwrite("m_balance", ...) // 编译错误!
private:
std::string m_name; // 私有成员
double m_balance; // 私有成员
// 私有方法
void audit() { /* 审计逻辑 */ }
};
PYBIND11_MODULE(private_members, m) {
py::class_<BankAccount>(m, "BankAccount")
.def(py::init<std::string, double>(),
py::arg("name"),
py::arg("balance") = 0.0,
"创建银行账户")
.def("name", &BankAccount::name, "获取账户名")
.def("balance", &BankAccount::balance, "获取余额")
.def("deposit", &BankAccount::deposit, py::arg("amount"), "存款")
.def("withdraw", &BankAccount::withdraw, py::arg("amount"), "取款")
.def("__repr__", [](const BankAccount& acc) {
return py::str("<BankAccount({}, {:.2f})>").format(acc.name(), acc.balance());
});
}
import private_members as pm
account = pm.BankAccount("Alice", 1000.0)
print(account) # <BankAccount(Alice, 1000.00)>
print(account.name()) # Alice
print(account.balance()) # 1000.0
account.deposit(500)
print(account.balance()) # 1500.0
account.withdraw(200)
print(account.balance()) # 1300.0
方式说明
公共 Getter/Setter标准 OOP 方式,推荐
def_readonly()绑定为只读属性
内部指针引用使用 py::return_value_policy

最佳实践: Python 不应该直接访问 C++ 的私有成员。通过设计良好的公共接口来控制访问,这样符合封装原则,也更安全。

本章涵盖了 pybind11 类绑定的核心概念:

主题关键点
类绑定py::class_<T>(m, "Name")
构造函数py::init<Args>(),支持重载
成员函数.def("name", &Class::method)
静态函数.def_static("name", &Class::static_method)
属性.def_property() / .def_property_readonly()
运算符py::self + py::self 等表达式
继承py::class_<Derived, Base>
虚函数覆写Trampoline 模式 + PYBIND11_OVERRIDE
抽象类纯虚函数 + trampoline
私有成员通过公共接口访问

下一章我们将学习如何导出 C++ 枚举和常量到 Python。