目录
import 在导入模块时,我们如何知道import在哪个路径下去寻找这个模块呢?先import sys,然后输入sys.path 就可以查看各个路径的优先顺序。sys.path.append("路径") 加入一个路径。import 导入一个模块时,会首先看当前路径下有没有这个模块,然后按照sys.path列表中顺序逐个查看列表中的路径 有没有这个模块。
0x00 重新导入模块:
当你在程序中导入了一个模块后,该模块被修改了,如何才能使用修改后的新模块呢?这是需要使用reload(模块)重新导入模块。reload 函数在 imp模块中,需要先导入imp模块
import test
test.test()
from imp import *
reload(test)
test.test()
0x01 循环导入
a.py
from b import b
def a():
print("aaaaaaa")
b()
b.py
from a import a
def b():
print("----bb----")
def c():
print("----c-----")
a()
我们需要从设计者的角度上来防止这种情况的发生。
0x02 作用域:
在ipython3中输入globals,打印当前命名空间中所有的全局变量
def test():
a = 100
b = 200
print(locals()) #打印该函数中所有的局部变量(名值对 封装在一个字典中)
#{'a':100,'b':200}
注意区分 python中局部变量的作用域 和 C++中局部变量的作用域的不同,C++中一个变量的作用域 是 它所直接属于的那个大括号,所括住的范围。因此在C++中for循环,while循环 中定义的变量都是局部变量 。而python中没有大括号这个概念,因此在python中for循环 和 while循环 中定义的变量 都是全局变量! 其余部分,例如 在函数中定义的变量,python和C++基本相同,即都认为是局部变量。
0x03 == 、is:
== 判断两个变量的值是否相等,is 判断两个变量 的地址是不是相同,即两个变量是不是指向同一个。(有一些小小的例外)
0x04 深拷贝 和 浅拷贝 (重点)
浅拷贝:只拷贝地址
a = [11,22,33]
b = a #浅拷贝,只是将a的地址拷贝了过来
深拷贝:开辟新的内存,拷贝原变量的内容到新的内存中
import copy
a = [11,22,33]
c = copy.deepcopy(a)#深拷贝
深拷贝注意点:
深拷贝将c中元素指向的内存空间 的内容也 实现了拷贝。(递归拷贝)
如果只想深拷贝一层,不实现递归拷贝,可以使用copy.copy()
copy.copy(原变量)可以识别原变量是否是可变类型, 如果是可变类型只拷贝一层内容,如果不是可变类型,一层内容都不拷贝,纯粹浅拷贝。
0x05 私有化
_x :单前置下划线的变量,通过 from xxx import * 禁止导入。但是通过import 模块名的方式可以导入。
__x:双前置下划线的变量,私有变量。只有在类内部可以访问。python是如何实现变量私有化的呢?其实python解释器进行了名字重整,将私有变量重新命名了,命名规则 _类名__私有变量。那么我们访问这个重命名的变量名 不就可以了吗!
0x06 property 的使用
class Money(object):
def __init__(self):
self.__money = 0
def getMoney(self):
return self.__money
def setMoney(self,value):
if isinstance(value,int):
self.__money = value
else:
print("不是整数类型")
money = property(getMoney,setMoney)
mon = Money()
mon.money = 100 #自动调用setMoney
print(mon.money) #自动调用 getMoney
注意点:
mon.money 到底是调用getMoney还是setMoney,要根据实际的场景来判断,
1.如果是给mon.money赋值,那么一定是调用setMoney()
2.如果是获取mon.money的值,那么一定调用getMoney()
property的作用:相当于把方法进行了封装,开发者在对属性设置的时候更加方便。
实现方法二:
class Money(object):
def __init__(self):
self.__money = 0
@property#该方法叫什么名,属性就是什么名,并且将该方法作为了get方法
def money(self):
return self.__money
@money.setter
def money(self,value):
if isinstance(value,int):
self.__money = value
else:
print("不是整数类型")
money = property(getMoney,setMoney)
mon = Money()
mon.money = 100 #自动调用setMoney
print(mon.money) #自动调用 getMoney
0x04python是动态语言
如何在运行过程中给对象添加属性呢?
class Person(object):
def __init__(self,newName,newAge):
self.name = newName
self.age = newAge
laowang = Person("laowang",99)
print(laowang.name)
print(laowang.age)
laowang.addr = "Beijing" #给对象添加实例属性
Person.num = 100 #给类添加类属性
print(laowang.num)
print(laozhang.num)
那么如何给对象添加方法呢?
class Person(object):
def __init__(self,newName,newAge):
self.name = newName
self.age = newAge
laowang = Person("laowang",99)
print(laowang.name)
print(laowang.age)
laowang.addr = "Beijing" #给对象添加实例属性
Person.num = 100 #给类添加类属性
print(laowang.num)
print(laozhang.num)
def run(self,distance):
print("ran %d m"%distance)
laowang.run = run #给对象添加实例方法
#laowang.run(10) #虽然laownag对象中 run属性已经指向了该函数,但是,这句代码还不正确,因为run属性指向的函数是后来添加的,即laowang.run()的时候,并没有把laowang自动作为第一个参数,导致该函数调用时出现缺少参数的问题。
#正确添加实例方法的方式
import types
laowang.run = types.MethodType(run,laowang)#给对象添加对象,表示将laowang作为第一个参数传给run
laowang.run(100)
#添加静态方法
@staticmethod
def test():
print("---static method---")
Person.test = test
Person.test()
#添加类方法
@classmethod
def printNum(cls):
print("---class method---")
Person.printNum = printNum
Person.printNum()
那么如何限制别人添加属性呢?可以使用__slots__
class Person(object):
__slots__ = ("name","age") #限制此类只能有name和age两个属性
laowang = Person()
laowang.name = "laowang"
laowang.age = 11
laowang.score =100 #报异常,注意__slots__定义的属性仅对当前类的实例起作用,对继承的子类的实例对象不起作用。
0x04生成器
#列表生成式
a = [x*2 for x in range(1000000)]
#一次性生成所有元素在一个列表中,但是如果要生成的列表太大,那么python解释器将会杀死程序。那么如何生成有100000个元素的列表呢?用生成器!
#生成器:将列表生成式的方括号修改成圆括号 就变成了一个 生成器对象
b = (x*2 for x in range(1000000))
print(next(b)) #每执行一次生成一个元素,next()函数的返回值就是这个元素
print(next(b)) #每执行一次生成一个元素
print(next(b)) #每执行一次生成一个元素
斐波那契数列:生成器的第二种方式(重点)
如果一个函数中有yield关键字,那么这个函数就被称之为生成器。
def createNum():
print("---start---")
a,b = 0,1
for I in range(5):
print("---1---")
yield b
print("---2---")
a,b = b,a+b
print("--stop---")
#createNum()#错误,如果函数出现yield,这就不是一个函数了,而是一个生成器
#生成器的返回值是一个生成器对象。
a = createNum()
for i in range(6):
print("第%d次"%i)
b = next(a) #等价于b = a.__next()
print(b)
"""
第0次
---start---
---1---
1
第1次
---2---
---1---
1
第2次
---2---
---1---
2
第3次
---2---
---1---
3
第4次
---2---
---1---
5
第5次
---2---
--stop---
Traceback (most recent call last):
File "fei.py", line 17, in <module>
b = next(a)
StopIteration
"""
当第一次调用next(a)时,程序会createNum从头开始执行,直到遇到yield b,就返回b,并且卡在这里。当下一次调用next(a)时,程序会从上一次卡住的地方继续执行。直到再一次遇到yield b卡住。但是可以看到程序出现了异常,为什么会报错呢?因为当主程序第六次循环时,createNum从上次卡住地方继续执行,这时它跳出了循环,无法返回一个yield b了。所以出现了异常。那么如果才能让程序不出现异常又打印出 stop呢?
def createNum():
print("---start---")
a,b = 0,1
for I in range(5):
print("---1---")
yield b
print("---2---")
a,b = b,a+b
print("--stop---")
#createNum()#错误,如果函数出现yield,这就不是一个函数了,而是返回生成器对象,不
能像调用函数一样调用
a = createNum()
for i in a:
print(i)
"""
---start---
---1---
1
---2---
---1---
1
---2---
---1---
2
---2---
---1---
3
---2---
---1---
5
---2---
--stop---
"""
使用send
def test():
I = 0
while I < 5:
temp = yield I #并不是将yield I 的返回值 赋值给了 temp,而是在下一次调用生成器时,可以给temp传一个值。
print(temp)
I+=1
t = test()
ret1 =t.__next__()
print(ret1)
ret2 = t.send("haha") #将值赋值给 temp,并从上一次函数停止的地方(上一次正好卡在了赋值的地方),开始继续执行直到遇到yield,然后,返回yield 之后的值。
print(ret2)
#注意send()的括号中必须有参数,如果你什么参数都不想传,那么你可以传一个None,表示空。给一个刚刚开始的生成器,用 t.send(None) 可以达到和 t.__next__() 相同的效果
多任务:看起来同时执行的任务
#协程
def test1():
while True:
print("1")
yield None
def test2():
while True:
print("2")
yield None
t1 = test1()
t2 = test2()
while True:
t1.__next__()
t2.__next__()
0x05 迭代器
什么是迭代?迭代即在上一次的xxx的基础上做下一次的xxx。例如版本的快速迭代,指的是在上一个版本的基础上开发下一个版本。在程序中,for i in range(10) 就是在上一次赋值的基础上,赋下一个值。
1.可迭代对象:
- 集合数据类型:如列表、元组、字典、集合、字符串等
- 生成器
#用 for循环 迭代 生成器
b = (x for x in range(10))
for temp in b:
print(temp)
2.判断一个对象是不是可以迭代:
from collection import Iterable
isinstance("abc",Iterable) #判断 字符串”abc“ 是不是 可迭代类 的一种实例
3.迭代器
可以被next() 函数调用 并不断返回下一个值的对象 称为 迭代器:iterator
生成器一定是迭代器
如何判断一个对象是不是一个迭代器对象呢?
from collections import Iterator
isinstance((x for x in range(10)),Iterator) #判断一个对象是不是迭代器对象
列表不是迭代器对象,但是可以强制类型转化为迭代器对象,转化之后占用的内存空间一定比原来要少
a = [11,22,33]
b = iter(a) #将a强制转换为迭代器 对象
next(b)
0x06 闭包
在python中,函数名 就是 函数指针,指向存储函数体代码的内存段。
def test():
print("---1----")
test() #调用该函数
b = test
b() #通过b加上括号同样可以调用该函数
那么什么是闭包呢?
python 中闭包的概念和js中闭包的概念几乎是完全相同的,友情链接:https://mp.csdn.net/postedit/91310802
闭包 = 函数地址 + 与之相关的环境
首先,请看如下代码:
def test():
num = "hello world"
def test2():
print(num)
test2()
test2()函数中并没有声明num 这个变量(像这种本地作用域中没有的变量在js中称为自由变量),调用test2()时怎么能直接输出num的值呢?很简单,test2的上层作用域中声明了这个变量num,并且变量num的作用域是整个test()函数,这其中就包括test2()函数。
那么如果我们这样写呢?
def test():
num = "hello world"
def test2():
print(num)
return test2
ret = test()
ret() #hello world
有人就要发问了,我们返回了test2函数的地址,然后通过地址来调用test2函数,这个时候,num是凭空出现的?怎么可能还能输出hello world呢?其实,不论是python还是js,在返回一个函数的引用的时候,还同时返回了 与之相关的一个环境, 这个环境中有 与这个函数相关的所有 变量的声明和定义,这个环境 闭合了函数中 所有的自由变量,让函数 变得 完整。函数的引用加上 环境 就称之为 一个闭包,也就说,看似只返回了 函数的引用,其实返回了 一个 闭包.
last but not least! python 中没有办法像 js中一样用闭包创建一个 计数器。报错 局部变量错误,具体原因我还在思考。。。嘿嘿嘿,有大佬知道麻烦留个言哈
0x07 装饰器
装饰器能干啥?装饰能在不改变原来函数定义的情况下,对函数进行重写。
装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以这也是python面试中必问的问题,但对于好多初次接触这个知识的人来讲,这个功能有点绕,自学时直接饶了过去,然后面试问到了就挂了,因为装饰器是程序开发的基础知识,这个都不会,别跟人家说你会python,看了下面的文章,保证你学会装饰器。
首先看懂以下代码:
def test1():
print("---1---")
def test1():
print("---2---")
test1() #---2--- 原因:python解释器首先开辟一段内存存储该函数的第一次定义,并将首地址赋值给test1这个变量,然后python解释器又开辟了一段内存来存储 test1()函数的第二次定义,并将首地址赋值给了test1这个变量,这次新的赋值 覆盖了 原来的赋值,所以现在 test1中存储的是 新的函数定义 的地址,所以自然而然 输入---2----
企业中写代码一般遵循“开放封闭”原则,所谓封闭,即已经实现的功能代码块你最好不要动,所谓开放,即你可以在已经实现的代码块不变更的情况下,对其扩展。
例如:有如下代码
def f1():
print("f1")
def f2():
print("f2")
def f3():
print("f3")
def f4():
print("f4")
f1()
f2()
f3()
f4()
现在需要在每个函数调用前加上一个验证身份的功能,让能验证通过的人才能调用这些函数,那么根据开放封闭原则,就不能如下这样改:
def check():
xxxxx
xxxxx
def f1():
check()
print("f1")
def f2():
check()
print("f2")
def f3():
check()
print("f3")
def f4():
check()
print("f4")
f1()
f2()
f3()
f4()
因为这样改,动了已经实现的代码块。那么应该怎么修改呢?这就要用到修饰器了
def w1(func):
def inner():
xxxxx
xxxxx
#验证功能
func()
return inner
@w1
def f1():
print("f1")
@w1
def f2():
print("f2")
@w1
def f3():
print("f3")
@w1
def f4():
print("f4")
f1()
f2()
f3()
f4()
那么装饰器的原理是什么呢?先来理解如下代码:
def w1(func):
def inner():
print("---正在验证权限---")
func()
return inner
def f1():
print("---f1---")
def f2():
print("---f2---")
innerFunc = w1(f1)
innerFunc()
那么假如将以上代码修改成这样呢?
def w1(func):
def inner():
print("---正在验证权限---")
func()
return inner
def f1():
print("---f1---")
def f2():
print("---f2---")
f1 = w1(f1) #对f1重新赋值
f1()
那么在函数上面写上@w1 就相当于 f1 = w1(f1)
那么假如一个函数加了两层装饰器该如何理解呢?
def makeBold(fn):
def wrapped():
print("111")
return "<b>"+fn()+"</bn>"
return wrapped
def makeItalic(fn):
def wrapped():
print("---2---")
return "<I>"+fn()+"</I>"
return wrapped
@makeBold #这个装饰器装饰的是 下面 这个装饰器 和函数组成的整体
@makeItalic #等价于 在函数后面加上 test3 = makeItalic(test3)
def test3():
print("---3----")
return "hello world"
print(test3())
"""
111
---2---
---3----
<b><I>hello world</I></bn>
"""
先从最近的装饰器开始装饰,然后逐个装饰较远的。
@makeBold
@makeItalic
def test3():
print("---3----")
return "hello world"
这段代码等价于:
def test3():
print("---3----")
return "hello world"
test3 = makeItalic(test3)
test3 = makeBold(test3)
那么如何对有参数的函数进行装饰呢?那还不简单,给内层函数加上两个参数不就可以了
def zhuangshi(func):
def inner(a,b):
print("zhuangshi")
func(a,b)
return inner
@zhuangshi
def test(a,b):
print(a)
print(b)
#test = zhuangshi(test)
test(1,2)
如果被装饰的函数有返回值,那么在重写内层函数的时候记得 将 原来函数的返回值 返回
def zhuangshi(func):
def inner(a,b):
print("zhuangshi")
ret = func(a,b)
return ret
return inner
@zhuangshi
def test(a,b):
print(a)
print(b)
return "haha"
#test = zhuangshi(test)
rtn = test(1,2)
print(rtn)
通用装饰器:在内层函数中加上不定长参数和return语句,那么这个装饰器可以适用于任何函数
def func(fuctionName):
def func_in(*args,**kwargs):
ret = functionName(*args,**kwargs)
return ret
return func_in
@func
def test():
print("hhh")
return "haha"
@func
def test2():
print("---test2---")
@func
def test3(a):
print(a)
带有参数的装饰器:带有参数的装饰器,能够实现 在运行时完成不同的功能
def zaitaoyiceng(canshu):
def func(fuctionName):
def func_in(*args,**kwargs):
if canshu == 123:
print("欢迎老板")
ret = functionName(*args,**kwargs)
else:
print("小臂崽子上线了?")
ret = functionName(*args,**kwargs)
return ret
return func_in
return func
"""
1.先执行zaitaoyiceng(123)函数,这个函数return的结果是func这个函数的引用
2.@func
3.使用@func对test()进行装饰
"""
@zaitaoyichang(123) #相当于先执行该函数,然后返回一个闭包 然后执行@func
def test():
print("hhh")
return "haha"
@zaitaoyichang(111)
def test2():
print("hh")
return "h"
test()
test2()