引言
我学过多门编程语言,却变得越来越迷惑。我们知道C语言,每个变量都需要声明变量类型,在函数调用的时候也必须保证参数类型一致。而python 的变量不需要声明类型,且甚至不许要提前声明,python极大的降低了程序开发的门槛(牺牲性能换来的)。本文着重解决2个问题:
- Python如何实现不需要声明数据类型
- python语言本身体现了哪些设计模式?
本文借由第一个问题探究python语言设计的底层实现,借由第二个问题探究python语言设计时的高层设计模式思想。
一、Python
Python是使用C语言编写的,同时也包含了很多用C语言编写的标准库。Python使用C语言的主要原因之一是因为C语言具有高效性和可移植性。Python的开源代码托管在GitHub上,其仓库地址为:https://github.com/python/cpython/
1. Python语言和C语言有什么不同?
-
语法:Python采用缩进风格,使用冒号来表示代码块,而C语言采用大括号来表示代码块。
-
类型:Python是动态类型语言,各种变量可以在运行时动态绑定不同的类型;而C语言是静态类型语言,变量类型在编译期间就确定了。
-
自动内存管理:Python自动进行内存管理,程序员不需要手动管理内存;而C语言需要手动进行内存管理,包括内存分配和释放。
-
编译和解释:C语言需要先编译成二进制机器码,再执行;而Python是解释型语言,直接由解释器解释执行。
-
应用场景:C语言适合编写操作系统、编译器等系统级应用;而Python适合用于数据分析、Web开发、人工智能等领域。
总体来说,Python是一门简单易学、高效率、高可读性的语言,适合于快速开发原型以及处理数据;而C语言是一门底层语言,可以直接操作硬件和内存,适合于编写高性能、底层系统级的程序。
2. Python如何实现不需要声明类型?
Python是一种动态类型语言,变量在使用之前不需要先声明数据类型,主要是通过以下方式进行实现的:
-
类型推断:Python在运行时通过分析代码语法和变量的初值等信息来推断其类型。这符合Python的“自然”哲学,同时也方便开发者理解和维护代码。
-
动态申请内存:Python在申请内存的时候,是根据变量类型来动态分配内存的。在赋值时如果值改变了类型,Python会自动释放原有内存并重新申请所需要的内存。
-
引用计数:Python使用引用计数来追踪变量的生命周期,每当一个对象被引用时其引用计数加1,每当一个对象失去一个引用时其引用计数减1。当引用计数为0时,Python将释放该对象占用的内存空间。
-
对象模型:Python的一切都是对象,代码的执行就是对象之间的交互和操作。因此,在Python中变量所引用的是一个对象,而不是一个内存地址,这样也就可以避免了其他语言中可能会出现的指针错误、内存泄漏等问题。
综上所述,Python通过类型推断、动态申请内存、引用计数以及对象模型等方式,实现了变量不需要声明类型的特性。
3. 如何理解Python的一切都是对象?
在Python中,“一切皆为对象”是一种重要的概念。这意味着,Python中的每个元素——包括数字、字符串、数据结构、函数、模块等等——都被视为一个对象。“对象”是一个抽象的概念,它包含了数据和行为。数据可以是各种类型的值,行为则是对象可以执行的动作或操作。在Python中,每个对象都有一个类型,这个类型定义了对象能够执行哪些操作和支持哪些方法。
在Python中,所有类都继承自一个名为object
的基类。这意味着,如果在Python中定义一个类但没有指定其任何超类,那么它将自动成为object
的子类。object
类包含了一些特殊方法(也称为“魔术方法”),这些方法对于对象的操作非常重要。例如,__init__()
方法用于对象的初始化,__str__()
方法用于将对象转换为字符串,__eq__()
方法用于比较两个对象是否相等等。
例1 执行如下代码:if(i==0)
在C语言中,上述代码将被编译为一条条件跳转指令(如JZ
或JE
),该指令会检查变量i
的值是否等于0,如果相等则跳转到if语句内部执行,否则跳过if语句。
在Python中,上述代码会被解释器解析为一条布尔比较操作 i==0
,然后根据比较的结果决定是否执行if语句内部的代码块。Python解释器会对 i
和 0
进行对象的比较操作,即执行i.__eq__(0)
根据比较结果决定是否执行if语句内的代码块。
例2 字符串拼接
C语言中底层实现比较原始,字符串是以字符数组的形式存在的。C语言中使用strcat()
方法将字符数组进行合并操作,合并后的字符串直接存放在原字符数组中,不会单独开辟一个新的内存空间。在C语言中,需要自己实现一个字符串的合并函数,而不能直接使用运算符重载来实现字符串相加。
python语言中可以通过str = str1+str2
直接进行字符串的拼接。使用的是运算符重载,即当执行 str1 + str2
时,Python 实际上会执行 str1.__add__(str2)
的操作,而不是简单的把两个字符串拼接起来,底层封装了增加新的内存、拷贝旧字符串、拼接新字符串、删除旧字符串等实现逻辑。代码实现上比较简单,但是由于字符串是不可变对象,每次相加时都会创建新的对象,会带来额外的内存开销。
下面是 Python 中 object
类的常用魔术方法(magic method)或特殊方法(special method)及其功能的总结:
魔术方法 | 功能 |
---|---|
__class__ |
调用 type() 函数访问对象的类属性 |
__delattr__(self, name) |
定义当试图删除一个对象的属性时要调用的方法 |
__dir__(self) |
定义如何响应独立于对象之外的 dir() 内置函数调用 |
__doc__ |
显示对象的文档字符串 |
__eq__(self, other) |
支持等于运算符(==) |
__format__(self, format) |
定义格式化对象时调用的方法,使用字符串的 format() 方法 |
__ge__(self, other) |
支持大于等于运算符(>=) |
__getattribute__(self, name) |
定义当用户试图访问一个对象的属性时要调用的方法 |
__gt__(self, other) |
支持大于运算符(>) |
__hash__(self) |
返回哈希值(必须是整数) |
__init__(self[, args...]) |
构造器,用于在创建对象时进行初始化操作 |
__init_subclass__() |
在此类(而不是实例)的子类被创建时被调用 |
__le__(self, other) |
支持小于等于运算符(<=) |
__lt__(self, other) |
支持小于运算符(<) |
__ne__(self, other) |
支持不等于运算符(!=) |
__new__(cls[, args...]) |
用于创建对象并返回该对象的方法 |
__reduce__(self) |
用于为 pickle 模块提供支持 |
__reduce_ex__(self, protocol) |
同上,但支持更多的协议 |
__repr__(self) |
用于为调试服务的字符串表示 |
__setattr__(self, name, value) |
定义当用户试图设置对象的属性时要调用的方法 |
__sizeof__(self) |
返回对象占用的内存字节数 |
__str__(self) |
用户获取对象的字符串表示 |
__subclasshook__(cls, subclass) |
在子类继承时调用,用于检查子类是否合法 |
需要注意的是,这些方法并不是全部都需要在我们自己的类中重载实现,而是根据实际的需要进行选择性实现。同时,Python中还有其他许多内置函数和内置类型,它们都有其特殊的用法和常用的魔术方法。
4.int类型
我们以整数类型 int
为例继续解释python的底层机制,int
继承自 object
类,并新增了一些内置函数用于整数类型的操作:
__add__(self, other)
:用于实现整数加法,相当于self + other
;__sub__(self, other)
:用于实现整数减法,相当于self - other
;__mul__(self, other)
:用于实现整数乘法,相当于self * other
;__truediv__(self, other)
:用于实现整数除法,相当于self / other
;__floordiv__(self, other)
:用于实现整数整除,相当于self // other
;__mod__(self, other)
:用于实现整数取模运算,相当于self % other
;__pow__(self, other[, modulo])
:用于实现整数幂运算,相当于self ** other
;__lshift__(self, other)
:用于实现按位左移运算,相当于self << other
;__rshift__(self, other)
:用于实现按位右移运算,相当于self >> other
;__and__(self, other)
:用于实现按位与运算,相当于self & other
;__or__(self, other)
:用于实现按位或运算,相当于self | other
;__xor__(self, other)
:用于实现按位异或运算,相当于self ^ other
;__invert__(self)
:用于实现按位取反运算,相当于~self
;__index__(self)
:将该整数转化为索引形式,以支持一些内置函数和方法,如list
、tuple
、dict
等。
这些内置函数使得整数对象可以支持不同的运算和操作,使得我们可以灵活地处理整数数据。
5.元类(metaclass)
Python 元类(metaclass)实际上就是用于创建类的类,它可以控制类的创建行为和属性,从而实现更高级别的面向对象编程。可以理解为类对象object
的’‘父类’'。在 Python 中,元类是非常重要的概念,可以通过以下属性来控制类的创建和行为:
方法名 | 描述 |
---|---|
__new__(cls, name, bases, attrs, **kwargs) |
用于创建并返回一个新的类对象 |
__init__(self, name, bases, attrs, **kwargs) |
用于初始化类对象,并可以对类对象的属性和方法进行修改 |
__call__(self, *args, **kwargs) |
用于自定义类对象实例化过程。包括实例的创建和初始化过程。它可以实现许多高级的面向对象编程功能,比如单例模式、对象池等 |
__getattribute__(self, name) |
用于获取对象的属性,元类可重载此方法来实现属性访问控制和管理 |
__setattr__(self, name, value) |
用于设置对象的属性,元类可重载此方法来实现更细粒度的属性设置控制和管理 |
__getattr__(self, name) |
用于获取对象的不存在的属性时,需要调用的函数,元类可重载此方法来实现属性的默认值、惰性计算等 |
6.Type()函数创建类
在 Python 内部实现中,object
类在 C 语言中定义,其数据结构实际上是一个 C 结构体(struct),其中储存了对象的类型信息和一些特殊的标志位。这个 C 结构体被称为 Python 对象头(object header),其定义如下:
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
其中,_PyObject_HEAD_EXTRA
表示一些额外的头部信息,ob_refcnt
表示对象的引用计数,ob_type
指向对象所属的类型信息,即对象的类对象。
在 Python 中,所有对象的类对象都是通过 type()
函数来创建的,包括 object
类本身。当我们定义一个新的类时,实际上是通过 type()
函数动态创建了一个新的类对象,并将其与类的名称进行绑定,从而创造出一个新的类。例如:
class MyClass:
pass
当执行以上代码时,Python 解释器会自动调用 type()
函数来创建一个名为 MyClass
的新类对象,然后将其绑定到一个变量名 MyClass
上,从而使得 MyClass
成为一个可用的类。可以使用 type()
函数来验证对象所属的类对象,例如:
obj = MyClass()
print(type(obj)) # <class '__main__.MyClass'>
print(type(MyClass)) # <class 'type'>
print(type(object)) # <class 'type'>
从输出结果可以看出,obj
是 MyClass
类对象的一个实例,而 MyClass
类本身是由 type
类创建的,而 type
类本身又是由 object
类创建的,这也说明了 object
类是 Python 中最基础的类。