Python中的getattr/setattr和pybind11中的attr相關函數
Python中的getattr/setattr和pybind11中的attr相關函數
- Python built-in functions
- python - getattr
- python - setattr
- 測試
- pybind11 - python_builtins
- pybind11 - getattr
- pybind11 - setattr
- 測試
- pybind11 - py::object_api::attr
- 測試
- 程式中用到的pybind11函數
- py::scoped_interpreter
- py::dict
- py::exec
- py::print
- py::str
Python built-in functions
python - getattr
getattr
屬於Python的built-in functions,參考其文檔python doc - getattr:
getattr(object, name)
getattr(object, name, default)
Return the value of the named attribute of object. name must be a string. If the string is the name of one of the object’s attributes, the result is the value of that attribute. For example, getattr(x, 'foobar') is equivalent to x.foobar. If the named attribute does not exist, default is returned if provided, otherwise AttributeError is raised. name need not be a Python identifier (see setattr()).Note Since private name mangling happens at compilation time, one must manually mangle a private attribute’s (attributes with two leading underscores) name in order to retrieve it with getattr().
回傳物件名稱為name
的屬性的值。
參數 name
必須是一個字串,如果這個字串是物件的一個屬性名稱,那麼結果就是該屬性的值。例如:
getattr(x, 'foobar') 等同於 x.foobar
如果指定名稱的屬性不存在,則:
- 若提供了
default
參數,則回傳default
; - 否則會拋出 AttributeError。
注意:name
不一定要是合法的 Python 標幟符號(identifier)(參見 setattr()
)。
另外,由於私有名稱改寫(private name mangling)發生在編譯時期,若要使用 getattr()
取得私有屬性(即名稱有兩個前導底線的屬性),必須手動改寫mangle屬性的名稱(如連結中的第三個例子所示)。
python - setattr
setattr
屬於Python的built-in functions,參考其文檔python doc - setattr:
setattr(object, name, value)
This is the counterpart of getattr(). The arguments are an object, a string, and an arbitrary value. The string may name an existing attribute or a new attribute. The function assigns the value to the attribute, provided the object allows it. For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123.name need not be a Python identifier as defined in Identifiers and keywords unless the object chooses to enforce that, for example in a custom __getattribute__() or via __slots__. An attribute whose name is not an identifier will not be accessible using the dot notation, but is accessible through getattr() etc..Note Since private name mangling happens at compilation time, one must manually mangle a private attribute’s (attributes with two leading underscores) name in order to set it with setattr().
這是 getattr()
的對應函式。
參數為:一個物件、一個字串,以及一個任意值。
- 這個字串可以是現有屬性的名稱,也可以是新的屬性名稱。
- 只要物件允許,函式會將值指派給該屬性。
- 例如:
setattr(x, 'foobar', 123)
等價於:
x.foobar = 123
name
不一定要是 Python 的識別字(identifier),除非物件選擇強制限制,例如透過自訂的__getattribute__()
或使用__slots__
。- 如果屬性名稱不是識別字,則無法用點號 (
.
) 來存取,但仍然可以透過getattr()
等函式存取。
注意:
- 私有名稱 (private name mangling) 是在 編譯時 發生的,
- 如果要用
setattr()
設定私有屬性(名稱前有兩個底線__
),必須手動進行名稱 mangling。
測試
編寫test.py
如下:
class Animal(object):legs = 4def run(self):print("I'm running!")if __name__ == "__main__":a = Animal();print("Initial leg value: ", getattr(a, "legs"))print("===================")setattr(a, "legs", 2)print("After set to 2:", getattr(a, "legs"))print("===================")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 0x7f343aa38730>>
call run method:
I'm running!
pybind11 - python_builtins
group python_builtins
Unless stated otherwise, the following C++ functions behave the same as their Python counterparts.
除非另有說明,下列 C++ 函式的行為與其對應的 Python 函式相同。
pybind11 - getattr
pybind11 doc - getattr
inline object getattr(handle obj, handle name)
inline object getattr(handle obj, const char *name)
inline object getattr(handle obj, handle name, handle default_)
inline object getattr(handle obj, const char *name, handle default_)
pybind11 - setattr
pybind11 doc - getattr
inline void setattr(handle obj, handle name, handle value)
inline void setattr(handle obj, const char *name, handle value)
測試
編輯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 定義 classpy::dict scope;py::exec(R"(
class Person:def __init__(self):self.name = "Alice"def greet(self):return "Hi, " + self.namep = Person()
)", scope);// 從 scope 取得 Python 物件py::object obj = scope["p"];// 使用 py::getattr / py::setattrpy::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;
}
在這段程式當中用到了py::scoped_interpreter
, py::dict
, py::exec
, py::print
, py::str
等函數,詳見程式中用到的pybind11函數。
嘗試過以下編譯指令但會出現linker error:
c++ -O3 -Wall -std=c++11 \demo.cpp -o demo \$(python3 -m pybind11 --includes) \$(python3-config --ldflags)
編譯:
c++ -O3 -Wall -std=c++11 demo.cpp -o demo \$(python3 -m pybind11 --includes) \-lpython3.8 -lpthread -ldl -lutil -lm
編譯成功後執行:
./demo
結果如下:
Via py::getattr: Alice
After py::setattr: Charlie
Greeting: Hi, Charlie
pybind11 - py::object_api::attr
pybind11 - py::object_api::attr
obj_attr_accessor attr(handle key) const
Return an internal functor to access the object’s attributes. Casting the returned detail::obj_attr_accessor instance to a handle or object subclass causes a corresponding call to getattr. Assigning a handle or object subclass causes a call to setattr.obj_attr_accessor attr(object &&key) const
See above (the only difference is that the key’s reference is stolen)str_attr_accessor attr(const char *key) const
See above (the only difference is that the key is provided as a string literal)
回傳一個 內部functor,用來存取物件的屬性。
將回傳的 detail::obj_attr_accessor
實例轉換(cast)成 handle
或 object
子類時,會觸發對應的 getattr
呼叫。
若將一個 handle
或 object
子類指定(assign)給它,則會觸發 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 定義 classpy::dict scope;py::exec(R"(
class Person:def __init__(self):self.name = "Alice"def greet(self):return "Hi, " + self.namep = 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")());return 0;
}
使用相同的指令編譯執行後結果如下:
Via .attr get: Alice
After .attr() set: Bob
Greeting: Hi, Bob
程式中用到的pybind11函數
py::scoped_interpreter
pybind11 - Embedding the interpreter
The interpreter must be initialized before using any Python API, which includes all the functions and classes in pybind11. The RAII guard class scoped_interpreter takes care of the interpreter lifetime. After the guard is destroyed, the interpreter shuts down and clears its memory. No Python functions can be called after this.
在使用任何 Python API 之前,必須先初始化直譯器,它包含了 pybind11 中的所有函式與類別。
RAII guard class scoped_interpreter
會負責管理直譯器的生命週期。
- 當這個守衛物件被銷毀時,直譯器就會關閉並清除記憶體。
- 在這之後,就不能再呼叫任何 Python 函式。
py::dict
pybind11 Reference - py::dict
class dict : public object
Subclassed by Dict< K, V >, kwargs
py::exec
pybind11 Utilities - Evaluating Python expressions from strings and files
pybind11 provides the eval, exec and eval_file functions to evaluate Python expressions and statements. The following example illustrates how they can be used.C++11 raw string literals are also supported and quite handy for this purpose. The only requirement is that the first statement must be on a new line following the raw string delimiter R"(, ensuring all lines have common leading indent
pybind11 提供了 eval
、exec
和 eval_file
這些函式,用來執行 Python 的運算式(expressions)與敘述(statements)。
C++11 的 raw string 字面值(raw string literals) 也被支援,並且在這種情境下非常方便。
唯一的要求是:第一個statememt必須從 raw string 分隔符 R"(
之後的 新的一行 開始,這樣可以確保所有程式碼行都有一致的縮排。
py::print
pybind11 Utilities - Using Python’s print function in C++
The usual way to write output in C++ is using std::cout while in Python one would use print. Since these methods use different buffers, mixing them can lead to output order issues. To resolve this, pybind11 modules can use the py::print() function which writes to Python’s sys.stdout for consistency.Python’s print function is replicated in the C++ API including optional keyword arguments sep, end, file, flush.
在 C++ 中,最常見的輸出方式是使用 std::cout
,而在 Python 中則是使用 print
。
由於這兩種方法使用不同的緩衝區,如果混用,可能會導致輸出順序錯亂。
為了解決這個問題,pybind11 模組可以使用 py::print()
,它會將輸出寫入 Python 的 sys.stdout
,確保輸出的順序與一致性。
此外,Python 的 print
函式 也在 C++ API 中被複製(重現),並支援選用的關鍵字參數:
sep
end
file
flush
py::str
pybind11 Reference - py::str
class str : public object
Public Functionsinline explicit str(handle h)
Return a string representation of the object. This is analogous to the str() function in Python.
返回物件的字串表示形式。這相當於 Python 中的 str()
函式。