Traducción de un artículo publicado específicamente para futuros alumnos del curso " Desarrollador Python, Profesional" .
, , Python ? , Lisp- , , ( Lisp ), Python , .
? , Python ? . -, , Python, / , - . -, , .
, , Python WebAssembly API bare bones C, , .
, , Python. , , , , . CPython , ( CPython 3.8.3, ).
, , , . , , , , , .
, :
obj.attr
, – . , :
>>> def example():
... obj.attr
...
>>> import dis
>>> dis.dis(example)
2 0 LOAD_GLOBAL 0 (obj)
2 LOAD_ATTR 1 (attr)
4 POP_TOP
6 LOAD_CONST 0 (None)
8 RETURN_VALUE
— LOADATTR. , , conames[i]
.
CPython Python/ceval.c. switch
, . , LOADATTR
:
case TARGET(LOAD_ATTR): {
PyObject *name = GETITEM(names, oparg);
PyObject *owner = TOP();
PyObject *res = PyObject_GetAttr(owner, name);
Py_DECREF(owner);
SET_TOP(res);
if (res == NULL)
goto error;
DISPATCH();
}
– , . – PyObject_GetAttr(), .
getattr(), , CPython. Python/bltinmodule.c, Python, , . «getattr»
, , «getattr» «builtin_getattr()»
static PyObject *
builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *v, *name, *result;
if (!_PyArg_CheckPositional("getattr", nargs, 2, 3))
return NULL;
v = args[0];
name = args[1];
if (!PyUnicode_Check(name)) {
PyErr_SetString(PyExc_TypeError,
"getattr(): attribute name must be string");
return NULL;
}
if (nargs > 2) {
if (_PyObject_LookupAttr(v, name, &result) == 0) {
PyObject *dflt = args[2];
Py_INCREF(dflt);
return dflt;
}
}
else {
result = PyObject_GetAttr(v, name);
}
return result;
}
, , , , getattr()
, PyObjectGetAttr().
? , , «» obj.attr
getattr(obj, "attr")
. , PyObjectGetAttr()
, , , , Python.
getattr()
, , , obj.attr
getattr(obj, "attr").
, CPython. , Python , , , , . «.»
, , , , .
getattr()
. -, , , . -, str
, TypeError
(, , ).
def getattr(obj: Any, attr: str, default: Any) -> Any:
if not isinstance(attr, str):
raise TypeError("getattr(): attribute name must be string")
... # Fill in with PyObject_GetAttr().
getattr()
. – getattribute(), . – getattr(), AttributeError
. ( ) , .
Python , . , , «»: – , – . , - type
, : type(obj)
.
(method resolution order, MRO). . , Python Dylan C3. Python MRO type(obj).mro()
.
, . , , , - . CPython , struct
. , , , .
getattr()
getattribute()
getattr()
, CPython , . , .
# Based on https://github.com/python/cpython/tree/v3.8.3.
from __future__ import annotations
import builtins
NOTHING = builtins.object() # C: NULL
def getattr(obj: Any, attr: str, default: Any = NOTHING) -> Any:
"""Implement attribute access via __getattribute__ and __getattr__."""
# Python/bltinmodule.c:builtin_getattr
if not isinstance(attr, str):
raise TypeError("getattr(): attribute name must be string")
obj_type_mro = type(obj).mro()
attr_exc = NOTHING
for base in obj_type_mro:
if "__getattribute__" in base.__dict__:
try:
return base.__dict__["__getattribute__"](obj, attr)
except AttributeError as exc:
attr_exc = exc
break
# Objects/typeobject.c:slot_tp_getattr_hook
# It is cheating to do this here as CPython actually rebinds the tp_getattro
# slot with a wrapper that handles __getattr__() when present.
for base in obj_type_mro:
if "__getattr__" in base.__dict__:
return base.__dict__["__getattr__"](obj, attr)
if default is not NOTHING:
return default
elif attr_exc is not NOTHING:
raise attr_exc
else:
raise AttributeError(f"{self.__name__!r} object has no attribute {attr!r}")
, getattr()
object.getattribute()
, getattr()
, , , Python , getattribute()
. object.getattribute()
.
, object.getattribute()
– . , – , . , , , Python, , : , classmethod staticmethod – .
: (non-data). get
, . set
del
, . – , classmethod
staticmethod
— .
, , – . , dict
, .
, , . , , , , , .
, , , . , :
;
;
;
.
, - , , , , , . , - . , self.attr = val init()
. , , , - . , , , , , .
def _mro_getattr(type_: Type, attr: str) -> Any:
"""Get an attribute from a type based on its MRO."""
for base in type_.mro():
if attr in base.__dict__:
return base.__dict__[attr]
else:
raise AttributeError(f"{type_.__name__!r} object has no attribute {attr!r}")
class object:
def __getattribute__(self, attr: str, /) -> Any:
"""Attribute access."""
# Objects/object.c:PyObject_GenericGetAttr
self_type = type(self)
if not isinstance(attr, str):
raise TypeError(
f"attribute name must be string, not {type(attr).__name__!r}"
)
type_attr = descriptor_type_get = NOTHING
try:
type_attr = _mro_getattr(self_type, attr)
except AttributeError:
pass # Hopefully an instance attribute.
else:
type_attr_type = type(type_attr)
try:
descriptor_type_get = _mro_getattr(type_attr_type, "__get__")
except AttributeError:
pass # At least a class attribute.
else:
# At least a non-data descriptor.
for base in type_attr_type.mro():
if "__set__" in base.__dict__ or "__delete__" in base.__dict__:
# Data descriptor.
return descriptor_type_get(type_attr, self, self_type)
if attr in self.__dict__:
# Instance attribute.
return self.__dict__[attr]
elif descriptor_type_get is not NOTHING:
# Non-data descriptor.
return descriptor_type_get(type_attr, self, self_type)
elif type_attr is not NOTHING:
# Class attribute.
return type_attr
else:
raise AttributeError(f"{self.__name__!r} object has no attribute {attr!r}")
object.getattribute()
, Python . , , , . Python, , .
, Python , «». Python 3, , , , .
«syntactic sugar» . .