常用的编程语言比如 java,javascript,python 等,我们可以从两个角度对语言进行分类。一个是动态语言 vs 静态语言,另一个是强类型 vs 弱类型。
- 动态语言还是静态语言指的是编译期还是运行期确定类型;
- 强类型和弱类型指的是是否会发生类型的隐式转换;
而 python 的语言类型定义是强类型的动态语言。
python 的变量python 的变量实质上是一个指针。
这不同于 java,在 java 中如果要声明一个字符串,那么这个字符串会复制给字符串的变量,这个变量也就不能接受其他类型的参数了,而 python 的变量仅仅是一个指针,可以接收任何类型的数据。
python 常见内置类型 常见内置类型1. 数值类型: int, float, complex, bool
2. 迭代类型: 所有实现迭代协议的类
3. 序列类型: list, bytes, bytearray, memoryview, range, tuple, str, array
4. 映射类型: dict
5. 集合类型: set, frozenset
6. 上下文管理类型: with 语句
7. 其他类型: 模块类型, class 和实例, 函数类型, 方法类型, 代码类型, object 对象, type 类型, ellipsis 类型, notimplemented 类型
类型和值的判断python 中常见的判断方法主要有 type,isinstance,==,is。其区别如下:
==
用来判断值是否相等
# 例子 a = [1, 2, 3] b = [1, 2, 3] print(a == b) # True,原理是调用了魔法函数 __eq__
is
用来确定是否是同一个类。
PS: python 中 None 是单例的,因此判断是否是 None 必须用 is(比较内存地址),而不是 ==(比较值)
type
用来判断是否是类的本身,比如 type(x) 的得到的结果为
isinstance
判断对象是否是类的实例。
判断对象类型的时候,这个函数会同时去检查类的继承链,这也就隐含这个多态的概念。
为什么下面的结果相等?
a = 1 b = 1 print(a is b)
python 中对于“小整数”或者“短字符串”在一定范围内会使用 intern 机制,这是 python 对于性能的一种优化机制,旨在提高运行的性能,因此结果为 True。
python 的作用域python 的作用域和 java 不一样,作用域由 def、class、lambda 等语句产生,if、try、for、with 等语句并不会产生新的作用域。
# if 语句不会生成新的作用域
if True:
msg = 'I am from Runoob'
print(msg)
# for 语句不会生成新的作用域
for item in [1, 3, 4]:
pass
abc = 123
print(abc)
print(item)
# try…catch 语句不会生成新的作用域
try:
edf = 456
except Exception as e:
pass
print(edf)
python 参数传递
python 参数传递的方式比较特殊,和其他语言进行区别,既不是“值传递”也不是“引用传递”。
python 参数传递的方式为 Call by Object(Call by Object Reference or Call by Sharing),也就是共享传参。函数形式参数获得实参中各个引用副本。
在进行函数传参的时候,python 是根据可变对象和不可变对象的特点让传递参数的行为类似于“引用传递”和“值传递”。
不可变对象:bool/int/float/tuple/str/frozenset
可变对象:list/set/dict
def add(a, b): a += b return a # 不可变类型 a = 1 b = 2 c = add(a, b) print(a, b, c) # 1 2 3 # 可变类型 a = [1, 2] b = [3, 4] c = add(a, b) print(a, b, c) # [1, 2, 3, 4] [3, 4] [1, 2, 3, 4] # 不可变类型 a = (1, 2) b = (3, 4) c = add(a, b) print(a, b, c) # (1, 2) (3, 4) (1, 2, 3, 4)
总结: python 传递参数的时候都是通过“对象引用”的方式进行传递的,也就是实参和形参都指向同一个对象,但是根据可变对象和不可变对象的特点表现出类似于“引用传递”和“值传递”的行为。
还有一个注意的点:默认参数只计算一次
# 通过函数举例 def first(l=[1]): l.append(1) print(l) first() first() # 结果如下: # [1, 1] # [1, 1, 1]
# 通过对象举例
class Company:
def __init__(self, name, staffs=[])
self.name = name
self.staffs = staffs
def add(self, staff_name):
self.staffs.append(staff_name)
def remove(self, staff_name):
self.staffs.remove(staff_name)
if __name__ == '__main__':
com1 = Company('com1')
com1.add('cc1')
print(com1.staffs) # ['cc1']
com2 = Company('com2')
com2.add('cc2')
print(com2.staffs) # ['cc1', 'cc2'],这个结果和预想的不一样
# 因为两次对象实例化都用了 Company.__init__.__defaults__ 中的默认变量
总结: 默认参数不推荐使用可变参数类型
函数传递中 *args, **kwargs 的含义是什么?
- *args 被打包成 tuple
- **kwargs 被打包成 dict
# *args 传递 print_args(*['a', 'b', 'c']) # **kwargs 传递 print_kwargs(**dict(a=1, b=2))异常处理 异常的继承等级
下面是 python 异常的继承等级的前几个。可以通过网址 https://docs.python.org/3/library/exceptions.html#exception-hierarchy 查看全部的异常继承等级。
在日常变成中最常用的就是 Exception,大多数的异常类都继承自这个类。
baseException
+-- SystemExit
+-- KeyboardInterrupt
+-- GeneratorExit
+-- Exception
+-- StopIteration
+-- StopAsyncIteration
+-- ...
为什么要继承 Exception 而不是 baseException?
一个简单的回答:如果继承的是 baseException 的话,ctrl + c 难道还能结束自己的程序吗?
使用异常的常见场景- 网络请求(超时、链接错误)
- 资源访问(权限问题、资源不存在)
- 代码逻辑(越界访问、KeyError等)
对于 python 中异常的捕获是和大多数编程语言一致的。都可以通过 try…catch 语句进行异常的捕获。
try:
# func
except (Exception1, Exception2) as e:
# 异常处理代码
else:
# pass
finally:
pass
Monkey patch
得益于动态语言的特性,所谓的 monkey patch 就是运行时替换的能力,也就是程序运行的时候,我们需要将一些内置的方法,类或者变量替换成自定义的。
比如 gevent 库在运行时需要可以修改内置的 socket 或 select。
# 运行时修改内置的 socket import socket print(socket.socket) #print("After monkey patch") from gevent import monkey monkey.patch_socket() print(socket.socket) # # 运行时修改内置的 select import select print(select.select) monkey.patch_select() print("After monkey patch") print(select.select) #
使用 time 演示 monkey patch 原理
import time
print(time.time())
def _time():
return 123
time.time = _time # 关键代码
print(time.time())
Introspection (内省)
得益于动态语言的特性,所谓的内省就 可以在运行时判断一个对象的类型。
ll = [1, 2, 3] dd = dict(a=1) # 在运行时判断对象类型的能力 print(type(ll)) print(type(dd)) print(isinstance(ll, list)) print(isinstance(dd, dict))



