J
Dynamic typing languages usually have only 1 type that is the object. This type has the infrastructure to manage the other types of data.I imagine you understand how a data is stored in memory, you know you need the type to know the size of the allocation to store. Because Python and other dynamic typing languages have only 1 type of data it becomes easy to do this, all basic objects are allocated in the same way with storage of the same size.When I am talking about allocation is space, not the management of this allocation because it is an orthogonal concept, and nothing has to do with dynamic typing, memory management can occur similarly in static typing languages. I say this because a comment talks about it and it's not what the question wants to know, she used the wrong tewrmo because she doesn't understand exactly what it is about.This object is typically a structure that needs two basic information (in the end I show specifically of Python as it is). You need an information of what kind of value you have in it and the value itself.This type that is marked is what determines the type you know in Python, so the typing is made in another layer. This can be 1 byte and each number indicates a different type. It doesn't have to be like this, but it's a simple and easy way to do.The other information is the value. This value can be a unique size, for example it can be 8 bytes.Then this set will always have 9 bytes. Okay, you know it will always allocate that size.But what does that value?If the guy is a boolean he will only use 1 of these 8 bytes and will have value 0 or 1. If you have a number you will have a form of floating point value or an integer as optimization (each language can do this or not) and if you have other basic numeric types you will do so and will occupy 1 to 8 bytes that has already been stored for you.Starts to change a bit when you have a string. This type is by reference and not by value, so the value there in those 8 bytes would have a pointer to the text. Types per reference always exist in two parts, the pointer and the object pointed out where the object actually has the data. There this extra allocation can be different size and is determined at the time of execution, so the most common is to be allocated in heap. This works equal in any other language that has no dynamic typing.The same goes for gold types by reference as the list and the dictionary, only the pointer to the object is that goes there, and in 8 bytes it fits any pointer.If you understand C this object would be more or less this:struct {
char type; //obviamente indica qual é o tipo
union { //só usa um dos valores abaixo, e sempre armazena o espaço para o maior deles
char boolean; //só se for o booleano
double number; //só se for um número
void *reference; //só se for um objeto por referência
} value;
} object;
Just to illustrate, it doesn't have to be exactly like this.This is a way to create a https://en.wikipedia.org/wiki/Tagged_union since C does not have this mechanism, in other languages typing could be more automatic.So in every code this is the object that will circulate. And every language knows that before accessing the value the code has to analyze what type is waiting in that context and the type of object that will manipulate to see if it can perform that operation or should make a mistake if they are incompatible. To know the type of that object is just looking at this field type that I showed up there, depending on the value there that is in it knows what type is (for example 0 would be a None, 1 would be a Boolean, 2, number, 3 string, 4, list, 5, dictionary, 6 object, etc.).The language ensures that every time you place a value there it will synchronize the type in the other field to always be compatible. If your code sends some change to the object the value will change and the type together.All this is done dynamically. This has nothing to do with the language being interpreted or compiled, it can do so in both, and in fact today it is common Python to run compiled.ConversionWhen the type is not what you want you have to use some function that manages another object for you. In some cases you may want to change the object itself (that is, it becomes the new type "every time"). In some operations it is possible to do only one reinterpretation, for example it can cause a boolean to be interpreted as a normal number without generating another number, after all a boolean is represented as a number so he knows what to do.Note that Python requires you to do this explicitly, after all it has strong typing, a concept that people often confuse.How curious when you have a guy object in Python, he carries internally one more guy he is, so we have 3 layers of type: that unique guy that Python uses to represent everything, the guy object and the type of what is more specifically this object (the name of the class). The first is not accessible or important for those who only use language.O Up and downcasting is used more on top of the types that are object. There casting of other types, but it is less common and in some cases actually requires conversion, not enough a reinterpretation. *casting of truth does not convert (sometimes we talk about casting when it converts, but it is only to simplify). This is not very different from other languages, it is only a matter of understanding how the data is actually stored.I don't want to get into so many details, but you can search the subject here on the site, I replied about it in https://pt.stackoverflow.com/q/336796/101 .In https://pt.stackoverflow.com/q/364148/101 has more information specifically about arrays, even has Python code excerpts showing how it actually stores objects.More details in https://pt.stackoverflow.com/q/324630/101 .Python StructuresThe real structure that Python uses should be this (if it hasn't changed when you're reading here long after, since this is internal language detail and they can change when and how they want to). It is not so simple, and in fact it has several parts, for example:typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
} PyObject;
Note that there is a field for the reference count that is the preferred form of Python garbage collection mechanism.Also:typedef struct {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
} PyVarObject;
Another:typedef struct _typeobject {
PyObject_VAR_HEAD
const char *tp_name; /* For printing, in format "<module>.<name>" */
Py_ssize_t tp_basicsize, tp_itemsize; /* For allocation */
/* Methods to implement standard operations */
destructor tp_dealloc;
Py_ssize_t tp_vectorcall_offset;
getattrfunc tp_getattr;
setattrfunc tp_setattr;
PyAsyncMethods *tp_as_async; /* formerly known as tp_compare (Python 2)
or tp_reserved (Python 3) */
reprfunc tp_repr;
/* Method suites for standard classes */
PyNumberMethods *tp_as_number;
PySequenceMethods *tp_as_sequence;
PyMappingMethods *tp_as_mapping;
/* More standard operations (here for binary compatibility) */
hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
getattrofunc tp_getattro;
setattrofunc tp_setattro;
/* Functions to access object as input/output buffer */
PyBufferProcs *tp_as_buffer;
/* Flags to define presence of optional/expanded features */
unsigned long tp_flags;
const char *tp_doc; /* Documentation string */
/* call function for all accessible objects */
traverseproc tp_traverse;
/* delete references to contained objects */
inquiry tp_clear;
/* rich comparisons */
richcmpfunc tp_richcompare;
/* weak reference enabler */
Py_ssize_t tp_weaklistoffset;
/* Iterators */
getiterfunc tp_iter;
iternextfunc tp_iternext;
/* Attribute descriptor and subclassing stuff */
struct PyMethodDef *tp_methods;
struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
descrgetfunc tp_descr_get;
descrsetfunc tp_descr_set;
Py_ssize_t tp_dictoffset;
initproc tp_init;
allocfunc tp_alloc;
newfunc tp_new;
freefunc tp_free; /* Low-level free-memory routine */
inquiry tp_is_gc; /* For PyObject_IS_GC */
PyObject *tp_bases;
PyObject *tp_mro; /* method resolution order */
PyObject *tp_cache;
PyObject *tp_subclasses;
PyObject *tp_weaklist;
destructor tp_del;
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
destructor tp_finalize;
} PyTypeObject;
One more used in certain cases:typedef struct {
PyObject_HEAD
Py_ssize_t length; /* Length of raw Unicode data in buffer */
Py_UNICODE str; / Raw Unicode buffer /
long hash; / Hash value; -1 if not set /
int state; / != 0 if interned. In this case the two
* references from the dictionary to this object
* are not counted in ob_refcnt. */
PyObject defenc; / (Default) Encoded version as Python
string, or NULL; this is used for
implementing the buffer protocol */
} PyUnicodeObject;
https://github.com/maniero/SOpt/blob/master/Python/Object.c .Have others, you can see in source code runtime of language.Sources: https://github.com/python/cpython/blob/master/Include/object.h https://github.com/python/cpython/blob/9e3e06e582accec82eb29cf665c3b4c7d84d2eb0/Doc/includes/typestruct.h https://github.com/python/cpython/blob/d6fb53fe42d83a10f1372dd92ffaa6a01d2feffb/Include/cpython/unicodeobject.h