【02】Python核⼼编程(全)
⼀、浅拷贝和深拷贝
1.浅拷贝
是对于⼀个对象的顶层拷贝,通俗的理解是:拷贝了引⽤,并没有拷贝内容。相当于把变量⾥⾯指向的⼀个地址给了另⼀个变量就是浅拷贝,⽽没有创建⼀个新的对象,如a=b。
2.深拷贝
⾸先要import copy,然后c = copy.deepcopy(a),就表⽰把a的内容深拷贝到c中,如果发现了a中也存在引⽤的内容,则递归拷贝,也就是把当前的这个引⽤的对象继续深拷贝。
3. copy和deepcopy的区别
①copy:浅拷贝,⾥⾯如果有可变类型,修改这个可变类型(如list),被拷贝的对象也会相应改变,仅仅拷第⼀层,如果是不可变类型,就⼀层都不拷,如果是可变类型就拷⼀层。
②deepcopy:深拷贝,⾥⾯不管是可变类型和不可变类型,被拷贝的对象都不会受到影响,递归拷贝。
使⽤copy模块的copy功能的时候,它会根据当前拷贝的数据类型是可变类型还是不可变类型有不同的处理⽅式,如元组是不可变类型,拷贝多份没有⽤,对copy来说,如果是可变类型就拷⼀层,如果是不可变类型,就⼀层都不拷。
⼆、属性property
1.属性property-1
①私有属性添加getter和setter⽅法
②使⽤property升级getter和setter⽅法
num = property(getNum,setNum)
注意:
降准是什么意思? Num到底是调⽤getNum()还是setNum(),要根据实际的场景来判断,值得注意的是⼀定要先填getNum后setNum。
如果是给t.num赋值,那么⼀定调⽤setNum()。
如果是获取t.num的值,那么就⼀定调⽤getNum()。
property的作⽤:相当于把⽅法进⾏了封装,开发者在对属性设置数据的时候更⽅便。
2.属性property-2
class Money(object):
@property #修饰器
def num(self):
print("------getter-----")
return self.__num
@num.setter #修饰器
def num(self,new_num):
print("------setter------")
self.__num = new_num
t.num = 20
print(t.num)
三、迭代器
1.迭代器
迭代是访问集合元素的⼀种⽅式。迭代器是⼀个可以记住遍历的位置的对象。迭代器对象从集合的第⼀个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不会后退。
2.可迭代对象(for 循环遍历的数据类型)
①⼀类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等。
②⼀类是 generator(列表⽣成式,⽣成器),包括⽣成器和带 yield 的generator function。
③这些可以直接作⽤于for循环的对象统称为可迭代对象:Iterable。
from collections import Iterable
# 如果可以迭代就返回True
isinstance([ ], Iterable)
3.判断是否可以迭代
可以使⽤isinstance()判断⼀个对象是否是Iterable对象:
from collections import Iterable
# 如果可以迭代就返回True
isinstance([ ], Iterable)
⽽⽣成器不但可以作⽤于for循环,还可以被next()函数不断调⽤并返回下⼀个值,直到最后抛出StopIteration错误表⽰⽆法继续返回下⼀个值了。
4.迭代器
①可以被next()函数调⽤并不断返回下⼀个值的对象称为迭代器:Iterator。
②可以使⽤isinstance()判断⼀个对象是否是Iterator对象。
③⽣成器(i for i in range(10))⼀定是迭代器,但迭代器不⼀定是⽣成器。
④迭代器⼀定是可迭代对象,但可迭代对象不⼀定是迭代器。
from collections import Iterator
isinstance((x for x in range(10)), Iterator) # 如果是的话就返回True
5.iter( )函数
①⽣成器都是Iterator(迭代器)对象,但 list、dict、str虽然是Iterable(可迭代),却不是Iterator(迭代器)。
②把list、dict、str 等 Iterable(可迭代)变成 Iterator(迭代器)可以使⽤iter()函数,就好⽐⼈可以游泳,但不是天⽣就会,可迭代对象就好⽐⼈,迭代器就好⽐会游泳的⼈,需要经过iter()训练⼀样。
isinstance(iter([ ]), Iterator)
True
四、闭包
1.函数的引⽤
test1() # 调⽤函数
ret = test1 # 引⽤函数
ret() # 通过引⽤调⽤函数
2.什么是闭包
def test(number):
print("-----1-----")
def test_in(number2):
print("----2-----")
房贷利息怎么算print(number+number2)
print("------3------")
# 把函数的引⽤返回了
return test_in
# ⽤来接收test(100),指向了⼀个函数体,这个100传给了number
ret = test(100)
# 这个1传给了number2
ret(1) # 这个返回101
ret(100) # 这个返回200
ret(200) # 这个返回300
3.闭包再理解
内部函数对外部函数作⽤域⾥变量的引⽤(⾮全局变量),则称内部函数为闭包
闭包的实际例⼦:
def line_conf(a, b):
def line(x):
return a*x + b
return line
line1 = line_conf(1, 1)
line2 = line_conf(4, 5)
print(line1(5))
print(line2(5))
这个例⼦中,函数line与变量a,b构成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提⾼代码可复⽤性的
作⽤
五、装饰器
1.装饰器
在有两个重名的函数中,Python解释器会调⽤最后定义的那重名函数,因为在Python⾥,第⼀个函数指向的是⼀⽚内存,然后⼜让这个函数指向另⼀⽚内存,就会利⽤第⼆⽚内存来执⾏,所有函数名应尽量避免相同
写代码要遵循开放封闭原则,虽然在这个原则是⽤的⾯向对象开发,但是也适⽤于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
①封闭:已实现的功能代码块
②开放:对扩展开发
③实例:
def w1(func):
def inner():
# 验证1
# 验证2
# 验证3
func()
return inner
@w1 # 装饰器
def f1():
print('f1')
@w1 # 装饰器
def f2():
print('f2')
........
对于上述代码,也是仅仅对基础平台的代码进⾏修改,就可以实现在其他⼈调⽤函数 f1、 f2 、f3 、f4 之前都进⾏【验证】操作,并且其他业务部门⽆需做任何操作
2.装饰器的功能
①引⼊⽇志
②函数执⾏时间统计
③执⾏函数前预备处理
④执⾏函数后清理功能
⑤权限校验等场景
⑤缓存
⑥如果是有多个装饰器的情况,⼀般是先装饰最下⾯的⼀个,然后依次往上,@w1类⽐于f1 = w1(f1)
3.装饰有参数的函数
在传递参数的时候,需要在闭包⾥⾯定义⼀个形参,闭包⾥⾯的调⽤的函数也要定义⼀个形参,否则会导致两部分函数调⽤失败
4.装饰不定长的参数的函数
在传递参数的时候,需要在闭包⾥⾯定义⼀个*args和**kwargs,闭包⾥⾯的调⽤的函数也要定义⼀个*args和**kwargs,这样就可以在调⽤的时候传递任意长度的参数,增加代码的可复⽤性
5.装饰带返回值的函数
需要在闭包⾥⾯进⾏⼀个接收,也就是ret = test(),然后再把接收到的ret return出去,这样在装饰的test才能返回出当前需要返回的东西,否则只会返回None
6.通⽤的装饰
def w1(func):
print("-----正在装饰-----")
def inner(*args,**kwargs):
print("---正在验证权限---")
print("----记录⽇志----")
ret = func(*args,**kwargs)
return ret
return inner
带有参数的装饰器:也就是在原来包含⼀个闭包的函数外⾯再给他套⼀个函数,⽤来传递装饰器的参数
def func_arg(arg):
def w1(func):
print("---记录⽇志---")
def inner(*args,**kwargs):
func(*args,**kwargs)
return inner
return w1
@func_arg("heihei")
def f1():
print("----f1----")
# 1.先执⾏func_arg("heihei")函数,这个函数return的结果是
# 2.@w1
# 3.使⽤@w1对f1进⾏装饰
# 作⽤:带有参数的装饰器,能够起到在运⾏时,有不同的功能
六、python是动态语⾔
1.Python是动态语⾔
动态编程语⾔是⾼级程序设计语⾔的⼀个类别,在计算机科学领域已被⼴泛应⽤。它是⼀类在运⾏时可以改变其结构的语⾔:例如新的函数、对象、甚⾄代码可以被引进,已有的函数可以被删除或是其他结构上的变化。这种动态语⾔的应⽤就好⽐是在没有更新app的情况下,它的界⾯在后台也可以被开发者更改,因为它是动态的,可以把新增的动态程序放置在⽂本,只要加载⼀遍即可
2.运⾏的过程中给对象绑定(添加)属性
也就是说给对象绑定⼀个实例属性(这个属性是初始化之外的额外属性),只有这个创建对象的属性如laozhao.addr = "北京"
3.运⾏的过程中给类绑定(添加)属性
如果需要所有的⼀个类的实例加上⼀个属性怎么办呢?答案就是直接给这个类绑定属性,如Person.sex = "male"
4.运⾏的过程中给类绑定(添加)⽅法
如果是对这个类绑定⼀个实例⽅法,那么就要先import types,然后如对象.⽅法名 = types.MethodType(函数名, 对象),把run这个⽅法绑定到P对象上。如果是静态⽅法和类⽅法,就直接⽤类名.⽅法名=函数名
5.运⾏的过程中删除属性、⽅法
①del 对象.属性名
②delattr(对象, "属性名")
七、__slots__的作⽤
1.动态语⾔
可以在运⾏的过程中,修改代码
2.静态语⾔
编译时已经确定好代码,运⾏过程中不能修改
3.__slots__的作⽤
为了达到限制的⽬的,Python允许在定义class的时候,定义⼀个特殊的__slots__变量,来限制该class实例能添加的属性,如__slots__ = ("name","age"),就可以达到限制name和age的属性,如果发现有添加其他属性的程序就会发⽣异常
4.使⽤__slots__注意
__slots__定义的属性仅对当前类实例起作⽤,对继承的⼦类是不起作⽤的
⼋、⽣成器
1.什么是⽣成器
通过列表⽣成式,我们可以直接创建⼀个列表。但是,受到内存限制,列表容量肯定是有限的。⽽且,创建⼀个包含100万个元素的列表,不仅占⽤很⼤的存储空间,如果我们仅仅需要访问前⾯⼏个元素,那后⾯绝⼤多数元素占⽤的空间都⽩⽩浪费了
①while的列表推导
list.append(i)
②for的列表推导,range与切⽚很类似
③第⼀个i是元素的值,后⾯的for是循环的次数,如果第⼀个i=11,那么所有的元素都是11
a=[i for i in range(1,18)]
④for控制循环的次数,for和if的嵌套
c = [i for i in range(10) if i%2==0]
⑤每执⾏第⼀个for循环都要执⾏第⼆个for循环的所有次数
d = [i for i in range(3) for j in range(2)]
⑥每执⾏第⼀个for循环都要执⾏第⼆个for循环的所有次数
d = [(i,j) for i in range(3) for j in range(2)]
例题:出100以内能被3整除的正整数
aiquot = []
for n in range(1,100)
if n%3 ==0:
aiquot.append(n)
range(3,100,3) # 很简洁
2.创建⽣成器⽅法1
要创建⼀个⽣成器,有很多种⽅法。第⼀种⽅法很简单,只要把⼀个列表⽣成式的[ ]改成( )
如L = [ x*2 for x in range(5)]和G = ( x*2 for x in range(5)),L是⼀个列表,⽽G是⼀个⽣成器,可以通过next(G)函数获得⽣成器的下⼀个返回值,不断调⽤ next()实在是太变态了,正确的⽅法是使⽤for循环,因为⽣成器也是可迭代对象
3.创建⽣成器⽅法2
fib函数变成generator,只需要把print(b)改为yield b就可以了,循环过程中不断调⽤yield ,就会不断中断。当然要给循环设置⼀个条件来退出循环,不然就会产⽣⼀个⽆限数列出来,当循环到没有元素的时候,将会⽣成异常,这时候就要⽤try和exception来检测异常,#print⾃动检测异常并停⽌,但是next()就要⽤try ,在创建⽣成器的时候需要接收函数的返回值
#1.next(返回函数名)
#2.返回函数名.__next__()是⼀样的⽅法来获取下⼀个返回值
总结:⽣成器是这样⼀个函数,它记住上⼀次返回时在函数体中的位置。对⽣成器函数的第⼆次或第 n 次调⽤跳转⾄该函数中间,⽽上次调⽤的所有局部变量都保持不变,⽣成器不仅记住了它数据状态;⽣成器还记住了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置
4.⽣成器的特点
①节约内存
②迭代到下⼀次的调⽤时,所使⽤的参数都是第⼀次所保留下的,即是说,在整个所有函数调⽤的参数都是第⼀次所调⽤时保留的,⽽不是新创建的
5.send⽤法
①如果在在程序中有个变量等于yield,不是说把yield的值给了这个变量,⽽是接下来在下⼀次调⽤执⾏⼀次的时候可以传⼀个值,t.send("haha")和t.__next__()都可以让⽣成器继续执⾏,不同的是send可以传递⼀个值,但是不能在程序刚开始执⾏就⽤send传值,有两种⽅法,要么先⽤__next__调⽤⼀次,再send⼀个值,或者t.send(None)
②⽣成器:完成多任务,控制多个任务执⾏的情况
如何阅读文章九、元类
1.元类
①类也是对象
②在⼤多数编程语⾔中,类就是⼀组⽤来描述如何⽣成⼀个对象的代码段,类同样也是⼀种对象
2.动态的创建类
因为类也是对象,你可以在运⾏时动态的创建它们,就像其他任何对象⼀样
def choose_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo # 返回的是类,不是类的实例自驾游车
else:
米醋泡黑豆class Bar(object):
pass
return Bar
MyClass = choose_class('foo') # 当你使⽤class关键字时,Python解释器⾃动创建这个对象
3.使⽤type创建类
①type还有⼀种完全不同的功能,动态的创建类,type可以像这样⼯作:
②type(类名, 由⽗类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
Test2 = type("Test2",(),{}) #定了⼀个Test2类
4.使⽤type创建带有属性的类
Foo = type('Foo', (), {'bar':True})
5.使⽤type创建带有⽅法的类
FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) # 这是添加实例⽅法echo_bar
Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "testStatic": testStatic}) # 添加静态⽅法
Foochild = type('Foochild',(Foo,),{"echo_bar":echo_bar, "testStatic":testStatic, "testClass":testClass}) # 添加类⽅法
6.到底什么是元类
元类就是⽤来创建类的东西,元类就是⽤来创建这些类 (对象) 的,元类就是类的类,元类⼜由元类创建,Python中所有的东西都是对象。这包括整数、字符串、函数以及类7.__metaclass__属性
__metaclass__ = something…
如果这么做了,Python就会⽤元类来创建类Foo,这⾥⾯有些技巧。⾸先写下class Foo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻__metaclass__属性,如果到了,Python就会⽤它来创建类Foo,如果没有到,就会⽤内建的type来创建这个类
⼗、GC垃圾回收
1.GC垃圾回收
①⼩整数对象池:Python为了优化速度,使⽤了⼩整数对象池,避免为整数频繁申请和销毁内存空间。Python 对⼩整数的定义是[-5, 257) 这些整数对象是提前建⽴好的,不会被垃圾回收
②⼤整数对象池:每⼀个⼤整数,均创建⼀个新的对象
③intern机制:假如要创建n个对象的是⼀样的字符串,那么python只会创建⼀个内存空间来存储,其他对象都是引⽤,但如果字符串中出现空格或其他符号就表⽰为不同的对象
2.GC(Garbage collection)垃圾回收
Python⾥也同Java⼀样采⽤了垃圾收集机制,不过不⼀样的是: Python采⽤的是引⽤计数机制为主,标记-清除和分代收集两种机制为辅的策略
3.引⽤计数机制的优点
①简单
②实时性:⼀旦没有引⽤,内存就直接释放了。不⽤像其他机制等到特定时机。实时性还带来⼀个好处:处理回收内存的时间分摊到了平时
4. 引⽤计数机制的缺点
①维护引⽤计数消耗资源
②循环引⽤
5.GC系统所承担的⼯作远⽐"垃圾回收"多得多。实际上,它们负责三个重要任务
①为新⽣成的对象分配内存
③从垃圾对象那回收内存
6.垃圾回收机制
Python中的垃圾回收是以引⽤计数为主,分代收集为辅
①导致引⽤计数+1的情况:
对象被创建,例如a=23
对象被引⽤,例如b=a
对象被作为参数,传⼊到⼀个函数中,例如func(a)
对象作为⼀个元素,存储在容器中,例如list1=[a,a]
②导致引⽤计数-1的情况:
对象的别名被显式销毁,例如del a
对象的别名被赋予新的对象,例如a=24
⼀个对象离开它的作⽤域,例如f函数执⾏完毕时,func函数中的局部变量(全局变量不会)
对象所在的容器被销毁,或从容器中删除对象
7.查看⼀个对象的引⽤计数
import sys
a = "hello world"
①可以查看a对象的引⽤计数,但是⽐正常计数⼤1,因为调⽤函数的时候传⼊a,这会让a的引⽤计数+1
②有三种情况会触发垃圾回收
调⽤gc.collect()
当gc模块的计数器达到阀值的时候
程序退出的时候
<模块的⾃动垃圾回收机制
①必须要import gc模块,并且is_enable()=True才会启动⾃动垃圾回收。
②这个机制的主要作⽤就是发现并处理不可达的垃圾对象。
③垃圾回收 = 垃圾检查 + 垃圾回收
④在Python中,采⽤分代收集的⽅法。把对象分为三代,⼀开始,对象在创建的时候,放在⼀代中,如果在⼀次⼀代的垃圾检查中,该对象存活下来,就会被放到⼆代中,同理在⼀次⼆代的垃圾检查中,该对象存活下来,就会被放到三代中
⑤gc模块⾥⾯会有⼀个长度为3的列表计数器,可以通过gc.get_count()获取,gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置⾃动执⾏垃圾回收的频率,例如(700,10,10)每⼀次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数⽬,700表⽰阈值,10表⽰没清理10次零代就清理⼀次⼆代,第⼆个10表⽰每清理10次⼀代链表就清理⼆代⼀次
注意点:
gc模块唯⼀处理不了的是循环引⽤的类都有__del__⽅法,所以项⽬中要避免定义__del__⽅法
⼗⼀、内建属性
常⽤专有属性说明触发⽅式
__init__ 构造初始化函数创建实例后,赋值时使⽤,在__new__后
__new__ ⽣成实例所需属性创建实例时
__class__ 实例所在的类实例.__class__
__str__ 实例字符串表⽰,可读性 print(类实例),如没实现,使⽤repr结果
__repr__ 实例字符串表⽰,准确性类实例回车或者 print(repr(类实例))
__del__ 析构 del删除实例
__dict__ 实例⾃定义属性 vars(实例.__dict__)
__doc__ 类⽂档,⼦类不继承 help(类或实例)
__getattribute__ 属性访问访问实例属性时
__bases__ 类的所有⽗类构成元素类名.__bases__
def __getattribute__(self,obj):
if obj == 'subject1':
江歌遇害案经过13分钟视频print('log subject1')
return 'redirect python'
else: # 测试时注释掉这2⾏,将不到subject2
return object.__getattribute__(self,obj)
__getattribute__的作⽤可以⽤来打印Log⽇志
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论