Python 语言特性:编译+解释、动态类型语言、动态语言
1. 解释性语言和编译性语言
- 1.1 定义
- 1.2 Python 属于编译型还是解释型?
- 1.3 收获
2. 动态类型语言
- 2.1 定义
- 2.2 比较
2. 动态语言(动态编程语言)
- 3.1 定义
- 3.2 Python 动态语言的体现
- 3.3 __slots__()
1. 解释性语言和编译性语言
1.1 定义
计算机是不能够识别高级语言的,所以当我们运行一个高级语言程序的时候,就需要一个“翻译机”来从事把高级语言转变成计算机能读懂的机器语言的过程。这个过程分成两类,一类是编译,一类是解释。
解释型语言
程序执行前不需要先进行编译,而是在执行时才通过解释器对代码进行翻译,翻译一句然后执行一句,直至结束。
编译型语言
编译性语言写的程序在被执行之前,需要一个专门的编译过程,把程序编译成为机器语言(二进制代码)的文件,比如 exe 文件,此后再运行时就不用重新翻译了,直接使用编译后的结果文件(exe 文件)来运行就行。
因为编译型语言在程序运行之前就已经对程序做出了“翻译”,所以在运行时就少掉了“翻译”的过程,因此效率比较高。但是我们也不能一概而论,一些解释型语言也可以通过解释器的优化来在对程序做出翻译时对整个程序做出优化,从而在效率上超过编译型语言。
此外,随着 Java 等基于虚拟机的语言的兴起,我们又不能把语言纯粹地分成解释型和编译型这两种。用 Java 来举例,Java 首先是通过编译器编译成字节码文件(不是二进制码),然后在运行时通过解释器(JVM)给解释成机器代码才能在各个平台执行,这同时也是 Java 跨平台的原因。所以我们说 Java 是一种先编译后解释的语言。
总结:将由高级语言编写的程序文件转换为可执行文件(二进制的)有两种方式,编译和解释,编译是在程序运行前,已经将程序全部转换成二进制码,而解释是在程序执行的时候,边翻译边执行。
- 编译型语言:执行效率高;依靠编译器,因此跨平台性差些。
- 解释型语言:执行效率低;依靠解释器,因此跨平台性好。
1.2 Python 属于编译型还是解释型?
其实 Python 和 Java 一样,也是一门基于虚拟机的语言,我们先来从表面上简单地了解一下 Python 程序的运行过程。
当我们在命令行中输入 python hello.py 时,其实是激活了 Python 的“解释器”,告诉“解释器”要开始工作了。可是在“解释”之前,其实执行的第一项工作和 Java 一样,是编译。
熟悉 Java 的同学可以想一下我们在命令行中如何执行一个 Java 的程序:
- javac hello.java(编译的过程)
- java hello(解释的过程
只是我们在用 Eclipse 等 IDE 时,将这两步给融合成了一步而已。其实 Python 也一样,当我们执行 python hello.py 时,他也一样执行了这么一个过程,所以我们应该这样来描述 Python,Python 是一门先编译后解释的语言。
1.2.1 简述 Python 的运行过程
在说这个问题之前,我们先来说两个概念,PyCodeObject 和 pyc 文件。
当 Python 程序首次运行时,编译的结果保存在位于内存中的 PyCodeObject 中。当 Python 程序运行结束时,Python 解释器则将 PyCodeObject 写回到硬盘中的 pyc 文件中。
当 Python 程序第二次运行时,首先程序会在硬盘中寻找 pyc 文件,如果找到则直接载入,否则就重复上面的过程。
所以我们应该这样来定位 PyCodeObject 和 pyc 文件,我们说 pyc 文件其实是 PyCodeObject 的一种持久化保存方式。
也就是说保存 pyc 文件是为了下次再次使用该脚本时避免重复编译,以此来节省时间。因此,只执行一次的脚本,就没必要保存其编译结果 pyc 文件,这样只是浪费空间。下面举例解释。
示例 1:执行不含“import”关键字的代码
a.py 代码如下:
print("hello world")
执行 a.py:
E:\test>python a.py
hello world
此时我们可以发现,在执行所在目录下并没有产生 pyc 文件,仍只有 a.py。
示例 2:执行含“import”关键字的代码
新增 b.py,代码如下:
import a
执行 b.py:
E:\test>python b.py
hello world
此时我们可以发现,pyc 文件产生了。
1.2.2 pyc 文件的目的是重用
编译型语言的优点在于,我们可以在程序运行时不用解释,而直接利用已经“翻译”过的文件。也就是说,我们之所以要把 py 文件编译成 pyc 文件,最大的优点在于我们在运行程序时,不需要重新对该模块进行重新的解释。
所以,我们需要编译成 pyc 文件的应该是那些可以重用的模块,这与我们在设计软件时是一样的目的。所以 Python 解释器认为:只有 import 进来的模块,才是需要被重用的模块。
这个时候也许有人会说,不对啊!你的这个问题没有被解释通啊,我的 test.py 不是也需要运行么,虽然不是一个模块,但是以后我每次运行也可以节省时间啊!OK,我们从实际情况出发,思考下我们在什么时候才可能运行 python xxx.py:
- 执行测试。
- 开启一个 web 进程。
- 执行一个程序脚本。
第一种情况(执行测试),这时哪怕所有的文件都没有 pyc 文件都是无所谓的。
第二种情况(开启一个 web 进程),我们试想一个 web 程序通常这样执行:
或
然后这个程序就类似于一个守护进程一样一直监听着 8181/9002 端口,而一旦中断,只可能是程序被杀死,或者其他的意外情况,那么你需要恢复要做的是把整个的 Web 服务重启。既然一直监听着,把 PyCodeObject 一直放在内存中就足够了,完全没必要持久化到硬盘上。
最后一种情况(执行一个程序脚本),一个程序的主入口其实很类似于 Web 程序中的 Controller,也就是说,它负责的应该是 Model 之间的调度,而不包含任何的主逻辑在内,如在 http://www.cnblogs.com/kym/archive/2010/07/19/1780407.html 中所提到,Controller 应该就是一个 Facade(外观模式),无任何的细节逻辑,只是把参数转来转去而已。做算法的同学可以知道,在一段算法脚本中,最容易改变的就是算法的各个参数,那么这个时候给持久化成 pyc 文件就未免有些画蛇添足了。
所以我们可以这样理解 Python 解释器的意图,Python 解释器只把我们可能重用到的模块持久化成 pyc 文件。
1.2.3 pyc 的过期时间
说完了 pyc 文件,可能有人会想到,每次 Python 的解释器都把模块给持久化成了 pyc 文件,那么当我的模块发生了改变的时候,是不是都要手动地把以前的 pyc 文件 remove 掉呢?
当然 Python 的设计者是不会犯这么白痴的错误的。而这个过程其实就取决于 PyCodeObject 是如何写入 pyc 文件中的。
我们来看一下 import 过程的源码吧:
这段代码比较长,我们只看标注的代码,其实它在写入 pyc 文件的时候,写了一个 Long 型变量,变量的内容则是文件的最近修改日期,同理,我们再看下加载 pyc 的代码:
不用仔细看代码,我们可以很清楚地看到原理,其实每次在加载 pyc 之前都会先检查一下 py 文件和 pyc 文件保存的最后修改日期,如果不一致则重新生成一份 pyc 文件。
1.3 收获
其实了解 Python 程序的执行过程对于大部分程序员(包括 Python 程序员)来说意义都是不大的,那么真正有意义的是,我们可以从 Python 的解释器的做法上学到什么,我认为有这样的几点:
- 其实 Python 是否保存成 pyc 文件和我们在设计缓存系统时是一样的,我们可以仔细想想,到底什么是值得扔在缓存里的,什么是不值得扔在缓存里的。
- 在跑一个耗时的 Python 脚本时,我们如何能够稍微压榨一些程序的运行时间,就是将模块从主模块分开(虽然往往这都不是瓶颈)。
- 在设计一个软件系统时,重用和非重用的东西是不是也应该分开来对待,这是软件设计原则的重要部分。
- 在设计缓存系统(或者其他系统)时,我们如何来避免程序的过期,其实 Python 的解释器也为我们提供了一个特别常见而且有效的解决方案。
2. 动态类型语言
2.1 定义
动态类型语言
所谓动态类型语言,就是(变量、属性、方法以及方法的返回值)类型的检查(确定)是在运行时才做。
即编译时与类型无关。一般在变量使用之前不需要声明变量类型,而变量的类型通常是由被赋的值的类型决定。 如 Php、Python 和 Ruby。
静态类型语言
与动态类型语言正好相反,在编译时便需要确定类型的语言。即写程序时需要明确声明变量类型。如 C/C++、Java、C# 等。
对于动态语言与静态语言的区分,套用一句流行的话就是:Static typing when possible, dynamic typing when needed。
强类型语言
强制数据类型定义的语言。也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。因此强类型定义语言是类型安全的语言。
弱类型语言
数据类型可以被忽略的语言。它与强类型定义语言相反, 一个变量可以赋不同数据类型的值。
强类型定义语言在速度上可能略逊色于弱类型定义语言,但是强类型定义语言带来的严谨性能够有效的避免许多错误。
Python 属于动态类型语言和弱类型语言。
2.2 比较
严格意义上,强类型与静态类型不是一回事,同理弱类型和动态类型。
- 强类型是指某门语言检查两种类型是否兼容,如果不兼容就抛出一个错误或强制类型转换,尽管这个说法并不是很严格。
- 静态类型强迫在类型结构的基础上执行多态。判断是否是一只鸭子的依据,是其基因蓝图(静态)还是因其叫声和走路的姿态像一只鸭子(动态)。
静态类型语言:
- 优点:在于其结构非常规范,突出显示代码以便于调试,方便类型安全。
- 缺点:需要写更多的类型相关代码(如声明变量),不便阅读(特别是当你看别人代码时,会连变量定义也看吗?想必不会,看结构,看方法的含义想必才是本质)。
动态类型语言:
- 优点:在于方便阅读,不需要写非常多的类型相关代码。
- 缺点:自然是不便调试,命名不规范时会造成读不懂,不利于理解等。
在强类型、静态类型语言的支持者,与动态类型、自由形式的支持者之间,经常发生争执:
- 前者主张,在编译的时候就可以较早发现错误,而且还可增进运行时期的性能。
- 后者主张,使用更加动态的类型系統,分析代码更为简单,减少出错机会,才能更加轻松快速的编写程序。
3. 动态语言(动态编程语言)
3.1 定义
根据维基百科,动态(编程)语言的定义如下:
动态编程语言是高级编程语言的一个类别,在计算机科学领域已被广泛应用。它是一类在运行时可以改变其结构的语言,例如新的函数、对象、甚至代码可以被引进,已有的类、函数也可以被删除或是其他结构上的变化。
动态语言目前非常具有活力,例如 JavaScript 便是一个动态语言,除此之外如 PHP、Ruby、Python 等也都属于动态语言,而 C、C++ 等语言则不属于动态语言。
3.2 python动态语言的体现
动态语言是一门在运行时可以改变其结构的语言,这句话如何理解?
示例 1:运行过程中给(实例)对象添加属性
class Person(object): def __init__(self, name=None, age=None):
self.name = name
self.age = age Jack = Person("Jack",18)
print(Jack.age)
在上述代码中,我们定义了 Person 类,然后创建了 Jack 对象,打印对象的 age 属性,这没毛病。现实中人除了名字和年龄,还会有其他属性,例如身高和体重。我们尝试打印一下身高属性。
print(Jack.height)
毫无疑问,这会报错,因为 Person 类中没有定义 height 属性。
但是如果在程序运行的时候添加 height 属性,会发生什么呢?
Jack.height = 170
print(Jack.height) # 输出结果:170 setattr(Jack, 'height', 170)
print(Jack.height) # 输出结果:170
在上述代码中,我们给 Jack 添加了 height 属性,然后打印,没有报错,可以输出结果。
我们再打印一下对象的属性:
print(Jack.__dict__) # 输出结果:{'name': 'Jack', 'age': 18, 'height': 170}
本来对象是没有 height 属性,但是可以在程序运行过程中给实例对象动态绑定属性,这就是动态语言的魅力。
需要注意:
Mia = Person('Mia', 18)
print(Mia.__dict__) # 输出结果:{'name': 'mia', 'age': 18}
Mia 对象居然没有 height 属性。为什么?事实上我们只是给类示例动态地绑定了一个属性,而不是给类绑定属性,所以重新创建的对象是没有 height 属性的。如果想要给类添加,也是可以的。
示例 2:动态给类添加属性
Person.height = None
Mia = Person("Mia", 18) print(Mia.height) # 输出结果:None
示例 3:动态给对象添加方法
class Person(object): def __init__(self,name=None,age=None):
self.name = name
self.age = age def speak_name(self):
print(self.name)
Jack = Person("Jack", 18)
Jack.speak_name = speak_name
Jack.speak_name(Jack) # Jack
print(Jack.__dict__) # {'name': 'Jack', 'age': 18, 'speak_name': <function speak_name at 0x000001F86CAE1E18>} Mia = Person("Mia", 18)
print(Mia.__dict__) # {'name': 'Mia', 'age': 18}
在上述代码中,对象 Jack 的属性中已经成功添加了 speak_name 函数。但是!有没有感觉 Jack.speak_name(Jack) 这个语句很别扭。按习惯来说,应该 Jack.speak_name() 就行了。如果想要达到这种效果,应该要像下面这样子做:
import types Jack.speak_name = types.MethodType(speak_name, Jack)
Jack.speak_name() # 输出结果:Jack
其中 MethodType 用于绑定方法对象。
示例 4:动态给类添加方法
import types class Person(object):
def __init__(self, name=None, age=None):
self.name = name
self.age = age def speak_ok(cls):
print(OK) Person.speak_name = types.MethodType(speak_ok, Person)
Person.speak_ok() # OK
示例 5:动态删除属性/方法
Mia = Person("Mia", 18)
delattr(Mia,'height') # 等价于 del Mia.height print(Mia.__dict__)
# 输出结果:{'name': 'mia', 'age': 18}
总结
- 给实例对象添加属性/方法:对象名.属性/方法名 = xxxx
- 给类对象添加属性/方法:类名.属性/方法名 = xxxx
- 给实例/类对象删除属性/方法:
- del 实例/类对象.属性/方法名
- delattr(实例/类对象, "属性/方法名")
3.3 __slots__()
通过以上例子可以得出一个结论:相对于动态语言,静态语言具有严谨性!所以,玩动态语言的时候,小心动态的坑!
如果我们想要限制实例对象的属性怎么办?比如,只允许对 Person 的实例对象添加 name 和 age 属性。
为了达到限制的目的,Python 允许在定义类的时候,定义一个 __slots__() 方法,来限制该实例对象能添加的属性:
>>> class Person:
... __slots__ = ("age", "name")
...
>>> p = Person()
>>> p.age = 12
>>> p.name = "xiaoming"
>>> p.hobby = "football"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute 'hobby'
注意:__slots__ 定义的属性仅对当前类的实例对象起作用,对继承的子类是不起作用的:
>>> class Student(Person):
... pass
...
>>> s = Student()
>>> s.hobby = "football"
>>>
Python 语言特性:编译+解释、动态类型语言、动态语言的更多相关文章
- C# 动态类型与动态编译简介
关于C#的动态类型与动态编译的简介,主要是一个Demo. 动态类型 关键字: dynamic 这里有详细的介绍:[C#基础知识系列]专题十七:深入理解动态类型 动态类型的应用场景 可以减少强制转换(强 ...
- OC 动态类型,动态绑定,动态加载
OC 动态类型,动态绑定,动态加载 Objective-C具有相当多的动态特性,基本的,也是经常被提到和用到的有 动态类型(Dynamic typing) 动态绑定(Dynamic binding) ...
- Python语言、编译解释、动态库静态库、编译过程、头文件
学习Python这门语言首先要了解 什么是编译与解释,什么是连接,什么是动态库与静态库, 什么是编译: 编译就是先把高级语言设计的程序翻译成二进制的机器语言,然后CPU直接执行机器码就可以了.一把翻译 ...
- 《精通C#》第十六章-动态类型和动态语言运行时-第一节至第四节
在.Net4.0中引入了一个关键字dynamic,这是一个动态类型关键字.Net中还有一个关键字是var,这是一个隐式类型,可以定义本地变量,此时var所代表的实际的数据类型有编译器在初次分配时决定, ...
- C# 4.0中的动态类型和动态编程
# 4.0的主题就是动态编程(Dynamic Programming).虽然C#仍然是一种静态语言,但是对象的意义开始变得越来越“动态”.它们的结构和行为无法通过静态类型来捕获,或者至少编译器在编译程 ...
- 动态类型识别&动态创建
以下大部分内容摘自<windows程序设计 第2版> 王艳平 张铮 编著 动态类型识别:在程序运行过程中,辨别对象是否属于特定类的技术. 应用举例:函数辨别参数类型.需要针对对象的类编写特 ...
- 与LINQ有关的语言特性
在说LINQ之前必须先说说几个重要的C#语言特性 一:与LINQ有关的语言特性 1.隐式类型 (1)源起 在隐式类型出现之前, 我们在声明一个变量的时候, 总是要为一个变量指定他的类型 甚至在fore ...
- Atitit.rust语言特性 attilax 总结
Atitit.rust语言特性 attilax 总结 1. 创建这个新语言的目的是为了解决一个顽疾:软件的演进速度大大低于硬件的演进,软件在语言级别上无法真正利用多核计算带来的性能提升.1 2. 不会 ...
- 偷窥篇:重要的C#语言特性——30分钟LINQ教程
本文转自:http://www.cnblogs.com/liulun/archive/2013/02/26/2909985.html 千万别被这个页面的滚动条吓到!!! 我相信你一定能在30分钟之内看 ...
- js:语言精髓笔记11--动态语言特性(1)
语言:程序最终被表达为数据(结构)和逻辑(算法),命令式和说明式/函数式语言分别从这两方面分类: 动态:在语言陈述时无法确定,必须在计算机执行时才能确定语言关系:JS是完全动态语言,导致其不确定性一般 ...
随机推荐
- docker+tomcat+jenkin实现立即构建Springboot项目
一.创建一个Springboot项目 1.编写pom.xml <groupId>com.zwhxpp</groupId> <artifactId>springboo ...
- JS中indexOf的用法
String.IndexOf(Char, [startIndex], [count]):返回指定字符在原字符串中的第一个匹配项的索引.可指定字符开始检索位置和指定长度的字符,若没有找到该字符,则返回 ...
- 手把手教你SpringBoot2整合Redis
此文仅为初学java的同学学习,大佬请勿喷,文末我会附上完整代码包供大家参考 redis的搭建教程此处略过,大家自行百度,本文的教程开始: 一.先在pom.xml中添加相关依赖 <!--redi ...
- AJAX基本操作
XMLHttpRequest对象: XMLHttpRequest 是 AJAX 的基础.所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject) ...
- 剑指 Offer 65. 不用加减乘除做加法 + 位运算
剑指 Offer 65. 不用加减乘除做加法 Offer_65 题目描述 题解分析 java代码 package com.walegarrett.offer; /** * @Author WaleGa ...
- 关于 C++ 中的强制转换 - 基础篇
引言 假设有基类 A,包含了虚函数 func1,以及有派生类 B,继承于类 A,派生类 B 中实现了函数 func1.此时可以用 A 类型的指针指向 B 类型的对象,并用 A 类型的指针调用 B 类型 ...
- 浅谈.Net Core后端单元测试
目录 1. 前言 2. 为什么需要单元测试 2.1 防止回归 2.2 减少代码耦合 3. 基本原则和规范 3.1 3A原则 3.2 尽量避免直接测试私有方法 3.3 重构原则 3.4 避免多个断言 3 ...
- 性能追击:万字长文30+图揭秘8大主流服务器程序线程模型 | Node.js,Apache,Nginx,Netty,Redis,Tomcat,MySQL,Zuul
本文为<高性能网络编程游记>的第六篇"性能追击:万字长文30+图揭秘8大主流服务器程序线程模型". 最近拍的照片比较少,不知道配什么图好,于是自己画了一个,凑合着用,让 ...
- WebSocket与即时通讯
HTTP 协议有一个缺陷:通信只能由客户端发起!HTTP 协议做不到服务器主动向客户端推送信息.这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦.我们只能使用"轮 ...
- FreeBSD 宣布 2020 年第 4 季度状态报告
FreeBSD 宣布 2020 年第 4 季度状态报告● 继续努力从 FreeBSD 基本系统中移除 GPL 协议的软件,以实现 FreeBSD 项目基本目标.● Linux 二进制兼容层的 Linu ...