python语言特点总结

python语言特性

关于python语言特性的一些总结:

  • 语言特点
  • 鸭子类型
  • 猴子补丁
  • 自省
  • 深浅拷贝
  • 列表和字典推导式
  • GIL
  • 函数传参
  • 生成器和迭代器,协程
  • 异常捕捉
  • 垃圾回收机制

1.python语言特点

  • 解释型的: python执行代码时是边解释边执行
  • 动态的: python是在编译时才会确定对象的类型
  • 强类型的: python不用声明对象的类型且没有隐式类型转换(像js可以隐式类型转换就是弱类型的语言)
    在这里插入图片描述

2.鸭子类型(多态)

  • python中更关注的是对象的行为而不是对象的类型;类和类之间不用共同继承一个父类,只要他们实现相同的行为即可;如果一种动物走起来像鸭子,叫起来也像鸭子,那就可以把它当成鸭子.
class Duck:
	def walk(self):
		print('i walk as a duck')
	def quack(self):
		print('i quack as a duck')

class Person:
	def walk(self):
		print('person walk like a duck')
	def quack(self):
		print('person quack like a duck')

d = Duck()
p = Person()

def test(s):
	s.walk()

test(d)
test(p)

在上面的代码中定义了一个duck类和person类,他们的行为一样;当一个函数调用鸭子类对象的walk和quak方法时,不用管传入的是不是鸭子类实例,只要是实现了walk方法的实例就可以当作鸭子类型的对象;就好比我们要创建一个迭代器对象,不用通过继承,只要这个对象实现__iter__和__next__方法,那它就是迭代器对象,就可以通过for来迭代取值.

3.猴子补丁

本质: 在代码运行时对对象进行修改或替换
在这里插入图片描述
应用场景: 在使用gevent模块时,输入以下代码:

from gevent import monkey
monkey.patch_all()

在运行时就可以给socket模块打补丁,将一些阻塞的操作替换为非阻塞的操作.

4.自省

自省 : 在程序运行时判断对象的类型.
自省的函数: id, type, isinstance, hasattr, getattr等

5.深浅拷贝

浅拷贝: 浅拷贝拷贝的是对象的引用;如a=b的赋值操作,列表的乘法运算时都是复制对象的引用
在这里插入图片描述
深拷贝: 深拷贝拷贝的整个对象,包括对象本身和对象中的引用
在这里插入图片描述
可以看到,变量a中包含其他对象的引用时,浅拷贝的对象只是复制了对象的引用,而深拷贝的对象是复制对象的引用和对象,所以它们的内存地址会不相等

6.列表推导和字典推导式

  • 列表推导式
l = [i for i in range(10)]
  • 字典推导式
a = {'a':1, 'b':2}
d = {i:j for i,j in a.items()}

7.GIL

GIL全局解释锁: GIL是cpython解释器的特性,不是python的特性;在python解释器cpython执行python代码时,为了保证线程的安全,在同一时刻只能有一个线程执行python字节码,所以每一个python程序执行时,线程都要先获取GIL锁,等该线程执行了一定时间或字节码后,或者遇到io操作时就会释放GIL锁,其他线程才能获取锁,因此python无法利用多核的优势.

既然python有GIL锁的存在,那线程在执行时是不是百分百安全的呢?
那也不一定,看以下代码:

a = 0
def f1():
	global a
	for i in range(10000):
		a += 1
def f2():
	global a
	for i in range(10000):
		a -= 1

t1 = Threading.thread(f1)
t2 = Threading.thread(f2)
t1.start()
t2.start()
t1.join()
t2.join()
print(a)

通过开启两个线程分别对变量a执行10000次加1和减1,按照推断最后a的值应该为0,但执行结果却是个随机数,并不为0,原因是a+=1的操作并非原子性的;假如在线程1执行完a加1后,还没来得及保存a的值,就切换到了线程2去执行a减1并且保存了a的变量,那再切换为线程1时,保存的a的值就不是原来a+1的值,所以有时候在执行非原子操作时,即使有GIL锁的存在,依然要加线程锁来保存线程的安全.

8.python中函数的传参

在python中函数的传参既不是传引用,也不是传值,而是传对象的引用或共享传参

def test(l):
	l.append(1)
	print(l)

lis = [2,3]
test(lis)
test(lis)

def test2(i):
	i += 2
	print(i)

a = 4
test2(a)
test2(a)

执行结果:

[2, 3, 1]
[2, 3, 1, 1]
6
6

理解:在执行test函数的时候,先创建了一个列表对象[2,3],将lis变量指向这个列表对象,由于函数传参传的是对象的引用,所以函数中的变量l也是指向列表对象,再执行append方法,由于列表是可变对象,所以列表对象添加了元素1,此时由于lis变量和l变量指向的是同一个对象,所以变量lis也变成了[2,3,1];但是在test2函数中,由于i指向的是不可变对象,所以在执行i+=2的操作时,就相当于重新创建了一个对象指向i,a的值并没有发生改变,所以两次执行的结果一样.

*args: 可变参数,可以接受多个位置参数,并将它们打包成一个元组传给函数
**kwargs: 关键字参数,可以接受多个关键字参数,并将它们打包成一个字典传给函数

9.生成器和迭代器

  • 迭代器: 实现了迭代器协议即__iter__和__next__方法的对象就是迭代器对象;迭代器可以记住迭代位置,通过next方法返回下一个值
  • 生成器: 当一个函数中包含yield关键字时,这个函数就是生成器;生成器函数在调用时返回的是一个生成器对象,生成器函数通过next方法启动函数,遇到yield就返回并暂停函数的执行,直到遇到下一个next
  • 基于生成器实现的协程: 对于python生成器中的yield来说,yield item会产出一个值,提供给next()的调用方,另外还会暂停生成器函数,让调用方继续执行,直到在调用下一个next再启动,调用方会从生成器中获取值.协程与生成器类似,都是定义体中包含yield关键字的函数;但是在协程中,yield通常出现在表达式的右边(如n=yield r),且调用方是通过send方法把数据传给生成器函数

如下面的基于协程的生产者-消费者模式:

# 生产者生成数据:
def produce(c):
    c.send(None)
    n = 0
    while n < 4:
        n += 1
        print('produce data %s.....' % n)
        print('send to customer')
        r = c.send(n)
        print('customer return %s' % r)
    c.close()


def customer():
    # r只是为了作为yield的返回值
    r = True
    while True:
        n1 = yield r
        if not n1:
            return
        print('customer doing %s' % n1)
        r = 'customer %s ok' % n1


g = customer()
produce(g)

执行结果如下:

produce data 1…
send to customer
customer doing 1
customer return customer 1 ok
produce data 2…
send to customer
customer doing 2
customer return customer 2 ok
produce data 3…
send to customer
customer doing 3
customer return customer 3 ok

生产者函数通过send(None)激活生成器函数即消费者函数,然后开始生产数据,通过send()方法将生产的数据发给消费者函数执行,再通过yield返回生产者函数生成数据,由于整个过程是单线程模式,不用通过加锁控制线程安全,不用进行线程的上下文切换, 大大提高了执行效率.

10. 异常捕捉

python中的异常捕捉机制try…except…:

def test_file(filename):
	try:
		f = open('filename', 'r')
		str = f.read()
	except Exception as e:
		print(e)
		str = ''
	else:
		print(str)
	finally:
		f.close()
  • try下面放可能出错的代码
  • 当try下面的代码发生异常,就执行except里的代码,捕捉异常
  • 当try下面的代码没有发生异常,就执行else里的代码
  • 不管代码有没有发生异常,都会执行finally下的代码

11.垃圾回收

python中的垃圾回收机制: 以引用计数为主,标记-清除和分代回收为辅,当一个对象的引用计数为0时,该对象的内存就会被python虚拟机回收.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值