import 导入模块
1.import导入模块
- import 导入模块(搜索路径)
import sys
sys.path
从列出的目录里依次查找要导入的模块文件
- 程序导入路径
因为sys.path返回的是一个列表,那么就可以用sys.path.append("需要导入的模块路径"),相对路径不用/,绝对路径/开头
- 重新导入模块
模块被导入后,如果某一方更新了这个模块,但是import module不能重新导入模块,重新导入需用from imp import *,然后再使用reload(test)即可重新导入
2.循环导入
循环导入就是程序在导入某个模块的过程中,该模块里的函数又需要导入自身的这个模块,如此进入死循环,
避免循环导入:所有在开发的过程中,开发者们写的模块不能相互调用,即应该相互隔开,然后由架构师来负责整体的模块调用使用,也就是上下设计分层,降低耦合
3.在一般情况下,如果仅仅是用作模块引入,不必写if __name__ =="__main__":
==和is
1.==和is
- is 是比较两个引用是否指向了同一个对象(引用比较)。a is b :返回False
- == 是比较两个对象是否相等 a ==b:返回True
- 总结:判断两者内容用==,判断两者是否指向同一个用is,如果是数字则在一个负数到正的一百二十多,==和is都为True,过了这个范围两者就不指向一个对象了
浅拷贝和深拷贝
1.浅拷贝和深拷贝
- 浅拷贝是对于一个对象的顶层拷贝,通俗的理解是:拷贝了引用,并没有拷贝内容
- 相当于把变量里面指向的一个地址给了另一个变量就是浅拷贝,而没有创建一个新的对象,如a=b
- 深拷贝首先要import copy,然后c = copy.deepcopy(a),就表示把a的内容深拷贝到c中,如果发现了a中也存在引用的内容,则递归拷贝,也就是把当前的这个引用的对象继续深拷贝
- copy和deepcopy的区别
copy:浅拷贝,里面如果有可变类型,修改这个可变类型(如list),被拷贝的对象也会相应改变,仅仅拷第一层,如果是不可变类型,就一层都不拷,如果是可变类型就拷一层
deepcopy:深拷贝,里面不管是可变类型和不可变类型,被拷贝的对象都不会受到影响,递归拷贝
- copy和deepcopy拷贝元组的特点
使用copy模块的copy功能的时候,它会根据当前拷贝的数据类型是可变类型还是不可变类型有不同的处理方式,如元组是不可变类型,拷贝多份没有用,对copy来说,如果是可变类型就拷一层,如果是不可变类型,就一层都不拷
进制丶位运算
1.进制间的转换
bin(10) #10进制转为2进制
int("1001",2) #2进制转为10进制
hex(10) #10进制转为16进制
int('ff',16) #16进制转为10进制
bin(0xa) #16进制转为2进制
oct(8) #10进制转为8进制
hex(0b1001) #2进制转为16进制,0b表示二进制
2.位运算的介绍
- & 按位与
- | 按位或
- ^ 按位异或
- ~ 按位取反
- << 按位左移
- >> 按位右移
私有化
1.私有化
- xx: 公有变量
- _x: 单前置下划线,私有化属性或方法,from somemodule import *禁止导入,类对象和子类可以访问
- __xx:双前置下划线,避免与子类中的属性命名冲突,无法在外部直接访问(名字重整所以访问不到)
- __xx__:双前后下划线,用户名字空间的魔法对象或属性。例如:__init__ , __ 不要自己发明这样的名字
- xx_:单后置下划线,用于避免与Python关键词的冲突
总结:
- 父类中属性名为__名字的,子类不继承,子类不能访问
- 如果在子类中向__名字赋值,那么会在子类中定义的一个与父类相同名字的属性
- _名的变量、函数、类在使用from xxx import *时都不会被导入
属性property
1.属性property-1
- 私有属性添加getter和setter方法
- 使用property升级getter和setter方法
#num = property(getNum,setNum)
#将方法转换为只读
#注意点:
#1.Num到底是调用getNum()还是setNum(),要根据实际的场景来判断,值得注意的是一定要先填getNum后setNum
#2.如果是给t.num赋值,那么一定调用setNum()
#3.如果是获取t.num的值,那么就一定调用getNum()
#property的作用:相当于把方法进行了封装,开发者在对属性设置数据的时候更方便
2.属性property-2
第二种的property的方法
@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 。
3.判断是否可以迭代
可以使用 isinstance() 判断一个对象是否是 Iterable 对象:
from collections import Iterable
isinstance([ ], Iterable),如果可以迭代就返回True
而生成器不但可以作用于 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 = test #引用函数
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, 看了下面的文章,保证你学会装饰器。
在有两个重名的函数中,Python解释器会调用最后定义的那重名函数,因为在python里,第一个函数指向的是一片内存,然后又让这个函数指向另一片内存,就会利用第二片内存来执行,所有函数名应尽量避免相同
写代码要遵循开放封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块
- 开放:对扩展开发
def inner():
# 验证1
# 验证2
# 验证3
func()
return inner
@w1 #装饰器
def f1():
print('f1')
@w1 #装饰器
def f2():
print('f2')
........
对于上述代码,也是仅仅对基础平台的代码进行修改,就可以实现在其他人调用函数 f1 f2 f3 f4 之前都进行【验证】操作,并且其他业务部门无需做任何操作
装饰器的功能:
- 引入日志
- 函数执行时间统计
- 执行函数前预备处理
- 执行函数后清理功能
- 权限校验等场景
- 缓存
如果是有多个装饰器的情况,一般是先装饰最下面的一个,然后依次往上,@w1类比于f1 = w1(f1)
装饰有参数的函数: 在传递参数的时候,需要在闭包里面定义一个形参,闭包里面的调用的函数也要定义一个形参,否则会导致两部分函数调用失败
装饰不定长的参数的函数: 在传递参数的时候,需要在闭包里面定义一个*args和**kwargs,闭包里面的调用的函数也要定义一个*args和**kwargs,这样就可以在调用的时候传递任意长度的参数,增加代码的可复用性
装饰带返回值的函数:需要在闭包里面进行一个接收,也就是ret = test(),然后再把接收到的return ret出去,这样在装饰的test才能返回出当前需要返回的东西,否则只会返回None
通用的装饰器:
例:def w1(func):
print("-----正在装饰-----")
def inner(*args,**kwargs):
print("---正在验证权限---")
print("----记录日志----")
ret = func(*args,**kwargs) #保存返回来的haha
return ret #把haha返回到17行的调用
return inner
带有参数的装饰器: 也就是在原来包含一个闭包的函数外面再给他套一个函数,用来传递装饰器的参数
如: def func_arg(arg):
def w1(func):
print("---记录日志---")
def inner(*args,**kwargs):
func(*args,**kwargs) #保存返回来的haha
return inner
return w1
@func_arg("heihei")
def f1():
print("----f1----")
#1.先执行func_arg("heihei")函数,这个函数return的结果是
#2.@w1
#3.使用@w1对f1进行装饰
作用:带有参数的装饰器,能够起到在运行时,有不同的功能
作用域
1.作用域
什么是命名空间:变量命名的范围(变量起作用的范围)
LEGB规则:Python 使用 LEGB 的顺序来查找一个符号对应的对象,即 locals -> enclosing function -> globals -> builtins
- locals,当前所在命名空间(如函数、模块),函数的参数也属于命名空间内的变量
- enclosing,外部嵌套函数的命名空间(闭包中常见)
- globals,全局变量,函数定义所在模块的命名空间
- builtins,内建模块的命名空间
比如:内建模块中有一个abs()函数,其功能求绝对值,如abs(-20)将返回20
python是动态语言
1.python是动态语言
动态编程语言 是 高级程序设计语言 的一个类别,在计算机科学领域已被广泛应用。它是一类 在运行时可以改变其结构的语言 :例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。动态语言目前非常具有活力。例如JavaScript便是一个动态语言,除此之外如 PHP 、 Ruby 、 Python 等也都属于动态语言,而 C 、 C++ 等语言则不属于动态语言,这种动态语言的应用就好比是在没有更新app的情况下,它的界面在后台也可以被开发者更改,因为它是动态的,可以把新增的动态程序放置在文本,只要加载一遍即可
运行的过程中给对象绑定(添加)属性:也就是说给对象绑定一个实例属性(这个属性是初始化之外的额外属性),只有这个创建对象的属性如laozhao.addr = "北京"
运行的过程中给类绑定(添加)属性:如果需要所有的一个类的实例加上一个属性怎么办呢? 答案就是直接给这个类绑定属性,如Person.sex = "male"
运行的过程中给类绑定(添加)方法: 如果是对这个类绑定一个实例方法,那么就要先import types,然后如 对象.方法名 = types.MethodType(函数名, 对象),把run这个方法绑定到P对象上。如果是静态方法和类方法,就直接用 类名. 方法名=函数名
运行的过程中删除属性、方法:
- del 对象.属性名
- delattr(对象, "属性名")
__slots__的作用
1.__slots__的作用
动态语言:可以在运行的过程中,修改代码
静态语言:编译时已经确定好代码,运行过程中不能修改
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性,如__slots__ = ("name","age"),就可以达到限制name和age的属性,如果发现有添加其他属性的程序就会发生异常
使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
生成器
1.生成器
什么是生成器:
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了
创建生成器方法1:
要创建一个生成器,有很多种方法。第一种方法很简单,只要把一个列表生成式的 [ ] 改成 ( )
如 L = [ x*2 for x in range(5)]和 G = ( x*2 for x in range(5)), L 是一个列表,而 G 是一个生成器,可以通过 next(G) 函数获得生成器的下一个返回值,不断调用 next() 实在是太变态了,正确的方法是使用 for 循环,因为生成器也是可迭代对象
创建生成器方法2:
fib函数变成generator,只需要把print(b)改为yield b就可以了,循环过程中不断调用 yield ,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来,当循环到没有元素的时候,将会生成异常,这时候就要用try和exception来检测异常,#print自动检测异常并停止,但是next()就要用try ,在创建生成器的时候需要接收函数的返回值#1.next(返回函数名) 和 #2.返回函数名.__next__()是一样的方法来获取下一个返回值
总结:生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变,生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置
生成器的特点:
- 节约内存
- 迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的
send用法:
如果在在程序中有个变量等于yield,不是说把yield的值给了这个变量,而是接下来在下一次调用执行一次的时候可以传一个值,t.send("haha")和t.__next__()都可以让生成器继续执行,不同的是send可以传递一个值
但是不能在程序刚开始执行就用send传值,有两种方法,要么先用__next__调用一次,再send一个值,或者t.send(None)
生成器.完成多任务:
控制多个任务执行的情况
类装饰器(扩展,非重点)
1.类装饰器(扩展,非重点)
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了 __call__() 方法,那么这个对象就是callable的
class Test(object):
def __init__(self, func):
print("---初始化---")
print("func name is %s"%func.__name__)
self.__func = func
def __call__(self):
print("---装饰器中的功能---")
self.__func()
#说明:
#1. 当用Test来装作装饰器对test函数进行装饰的时候,首先会创建Test的实例对象
# 并且会把test这个函数名当做参数传递到__init__方法中
# 即在__init__方法中的func变量指向了test函数体
#
#2. test函数相当于指向了用Test创建出来的实例对象
#
#3. 当在使用test()进行调用时,就相当于让这个对象(),因此会调用这个对象的__call__方法
#
#4. 为了能够在__call__方法中调用原来test指向的函数体,所以在__init__方法中就需要一个实例属性来保存这个函数体的引用
# 所以才有了self.__func = func这句代码,从而在调用__call__方法中能够调用到test之前的函数体
@Test
def test():
print("----test---")
test()
showpy()#如果把这句话注释,重新运行程序,依然会看到"--初始化--"
元类
1.元类
类也是对象:
在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段,类同样也是一种对象
动态的创建类:
因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样
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解释器自动创建这个对象
2.使用type创建类:
type还有一种完全不同的功能,动态的创建类,type可以像这样工作:
type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
如:Test2 = type("Test2",(),{}) #定了一个Test2类
3. 使用type创建带有属性的类:
Foo = type('Foo', (), {'bar':True})
4.使用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}),添加类方法
5.到底什么是元类:
元类就是用来创建类的“东西,元类就是用来创建这些类(对象)的,元类就是类的类,元类又由元类创建,Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类
__metaclass__属性:
class Foo(object):
__metaclass__ = something…
如果你这么做了,Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下class Foo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类
GC垃圾回收
1.GC垃圾回收
小整数对象池:Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。
Python 对小整数的定义是 [-5, 257) 这些整数对象是提前建立好的,不会被垃圾回收
大整数对象池:每一个大整数,均创建一个新的对象
intern机制:假如要创建n个对象的是一样的字符串,那么python只会创建一个内存空间来存储,其他对象都是引用,但如果字符串中出现空格或其他符号就表示为不同的对象
GC(Garbage collection)垃圾回收:
python里也同java一样采用了垃圾收集机制,不过不一样的是: python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
2.引用计数机制的优点:
- 简单
- 实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时
- 维护引用计数消耗资源
- 循环引用
- 为新生成的对象分配内存
- 识别那些垃圾对象,并且
- 从垃圾对象那回收内存
导致引用计数+1的情况:
- 对象被创建,例如a=23
- 对象被引用,例如b=a
- 对象被作为参数,传入到一个函数中,例如func(a)
- 对象作为一个元素,存储在容器中,例如list1=[a,a]
- 对象的别名被显式销毁,例如del a
- 对象的别名被赋予新的对象,例如a=24
- 一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
- 对象所在的容器被销毁,或从容器中删除对象
import sys
a = "hello world"
sys.getrefcount(a)
可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1
有三种情况会触发垃圾回收:
- 调用gc.collect(),
- 当gc模块的计数器达到阀值的时候。
- 程序退出的时候
- 必须要import gc模块,并且is_enable()=True才会启动自动垃圾回收。
- 这个机制的主要作用就是发现并处理不可达的垃圾对象。
- 垃圾回收=垃圾检查+垃圾回收
gc模块里面会有一个长度为3的列表的计数器,可以通过gc.get_count()获取,gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置自动执行垃圾回收的频率,例如(700,10,10) 每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,700表示阈值,10表示没清理10次零代就清理一次二代,第二个10表示每清理10次一代链表就清理二代一次
注意点:gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法
内建属性和函数
1.内建属性
常用专有属性 说明 触发方式
__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':
print('log subject1')
return 'redirect python'
else: #测试时注释掉这2行,将找不到subject2
return object.__getattribute__(self,obj)
- __getattribute__的作用可以用来打印Log日志
class Person(object):
def __getattribute__(self,obj):
print("---test---")
if obj.startswith("a"):
return "hahha"
else:
return self.test
def test(self):
print("heihei")
t.Person()
t.a #返回hahha
t.b #会让程序死掉
#原因是:当t.b执行时,会调用Person类中定义的__getattribute__方法,但是在这个方法的执行过程中
#if条件不满足,所以 程序执行else里面的代码,即return self.test 问题就在这,因为return 需要把
#self.test的值返回,那么首先要获取self.test的值,因为self此时就是t这个对象,所以self.test就是
#t.test 此时要获取t这个对象的test属性,那么就会跳转到__getattribute__方法去执行,即此时产
#生了递归调用,由于这个递归过程中 没有判断什么时候推出,所以这个程序会永无休止的运行下去,又因为
#每次调用函数,就需要保存一些数据,那么随着调用的次数越来越多,最终内存吃光,所以程序 崩溃
#
# 注意:以后不要在__getattribute__方法中调用self.xxxx
2.内建函数
- range(start, stop[, step]) -> list of integers
stop:到stop结束,但不包括stop.例如:range(0, 5) 是[0, 1, 2, 3, 4]没有5
step:每次跳跃的间距,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1)
- map函数
map(...)
map(function, sequence[, sequence, ...]) -> list
function:是一个函数
sequence:是一个或多个序列,取决于function需要几个参数
返回值是一个list
- filter函数
filter(...)
filter(function or None, sequence) -> list, tuple, or string
function:接受一个参数,返回布尔值True或False
sequence:序列可以是str,tuple,list
filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素。
- reduce函数
reduce(...)
reduce(function, sequence[, initial]) -> value
function:该函数有两个参数
sequence:序列可以是str,tuple,list
initial:固定初始值
reduce依次从sequence中取一个元素,和上一次调用function的结果做参数再次调用function。 第一次调用 function时,如果提供initial参数,会以sequence中的第一个元素和initial 作为参数调用function,否则会以序列sequence中的前两个元素做参数调用function。 注意function函数不能为None
- sorted函数
sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list
调试
1.调试
pdb是基于命令行的调试工具,非常类似gnu的gdb(调试c/c++)执行时调试
程序启动,停止在第一行等待单步调试
python -m pdb xxx.py,
n(next)执行下一步,l(list)显示当前执行进度,c(continue)继续执行代码,b(break)添加断点,q(quit)中止并退出,clear num删除指定断点,p(print)打印变量的值,a(args)打印所有的形参数据,s(step)进入到一个函数,r执行代码直到从当前函数返回