python 的语言特性

python 语言类型鉴别

常用的编程语言比如 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 中常见的判断方法主要有 typeisinstance==is。其区别如下:

==

用来判断值是否相等

# 例子
a = [1, 2, 3]
b = [1, 2, 3]
print(a == b) # True,原理是调用了魔法函数 __eq__

is

用来确定是否是同一个类。

PS: python 中 None 是单例的,因此判断是否是 None 必须用 is(比较内存地址),而不是 ==(比较值)

type

用来判断是否是类的本身,比如 type(x) 的得到的结果为 <class '__main__.X'>

isinstance

判断对象是否是类的实例。

判断对象类型的时候,这个函数会同时去检查类的继承链,这也就隐含这个多态的概念。

为什么下面的结果相等?

a = 1
b = 1
print(a is b)

python 中对于“小整数”或者“短字符串”在一定范围内会使用 intern 机制,这是 python 对于性能的一种优化机制,旨在提高运行的性能,因此结果为 True。

python 的作用域

python 的作用域和 java 不一样,作用域由 defclasslambda 等语句产生,iftryforwith 等语句并不会产生新的作用域。

# 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)  # <class 'socket.socket'>

print("After monkey patch")
from gevent import monkey
monkey.patch_socket()
print(socket.socket)  # <class 'gevent._socket3.socket'>

# 运行时修改内置的 select
import select
print(select.select)
monkey.patch_select()

print("After monkey patch")
print(select.select)  # <function select at 0x7f82e7f33560>

使用 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))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值