1.1==,is的使用
·is是比较两个引用是否指向了同一个对象(引用比较)。
·==是比较两个对象是否相等。
1.2深拷贝、浅拷贝
1.2.1浅拷贝
浅拷贝是对于一个对象的顶层拷贝
拷贝了引用,并没有拷贝内容
1.2.2深拷贝
深拷贝是对于一个对象所有层次的拷贝(递归)
进一步理解拷贝
In [23]: a = [11,22,33]
In [24]: b = [44,55,66]
In [25]: c = (a,b)
In [26]: e = copy.deepcopy(c)
In [27]: a.append(77)
In [28]: a
Out[28]: [11,22,33,77]
In [29]: b
Out[29]: [44,55,66]
In [30]: c
Out[30]: ([11,22,33,77], [44,55,66])
In [31]: e
Out[31]: ([11,22,33], [44,55,66])
In [32]:
In [32]:
In [32]: f = copy.copy(c)
In [33]: a.append(88)
In [34]: a
Out[34]: [11,22,33,77,88]
In [35]: b
Out[35]: [44,55,66]
In [36]: c
Out[36]: ([11,22,33,77,88], [44,55,66])
In [37]: e
Out[37]: ([11,22,33], [44,55,66])
In [38]: f
Out[38]: ([11,22,33,77,88], [44,55,66])
1.2.3拷贝的其他方式
浅拷贝对不可变类型和可变类型的copy不同
In [88]: a = [11,22,33]
In [89]: b =copy.copy(a)
In [90]: id(a)
Out[90]:59275144
In [91]: id(b)
Out[91]:59525600
In [92]:
a.append(44)
In [93]: a
Out[93]: [11,22,33,44]
In [94]: b
Out[94]: [11,22,33]
In [95]:
In [95]:
In [95]: a = (11,22,33)
In [96]: b =copy.copy(a)
In [97]: id(a)
Out[97]:58890680
In [98]: id(b)
Out[98]:58890680
·分片表达式可以赋值一个序列
a ="abc"
b = a[:]
·字典的copy方法可以拷贝一个字典
d = dict(name="zhangsan", age=27)
co = d.copy()
·有些内置函数可以生成拷贝(list)
a = list(range(10))
b = list(a)
·copy模块中的copy函数
importcopy
a = (1,2,3)
b = copy.copy(a)
1.3属性property
1.3.1私有属性添加getter和setter方法
classMoney(object):
def__init__(self):
self.__money =0
defgetMoney(self):
returnself.__money
defsetMoney(self, value):
ifisinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
1.3.2使用property升级getter和setter方法
classMoney(object):
def__init__(self):
self.__money =0
defgetMoney(self):
returnself.__money
defsetMoney(self, value):
ifisinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
money = property(getMoney, setMoney)
运行结果:
In [1]:fromget_setimportMoney
In [2]:
In [2]: a = Money()
In [3]:
In [3]: a.money
Out[3]:0
In [4]: a.money =100
In [5]: a.money
Out[5]:100
In [6]: a.getMoney()
Out[6]:100
1.3.3使用property取代getter和setter方法
@property成为属性函数,可以对属性赋值时做必要的检查,并保证代码的清晰短小,主要有2个作用:
·将方法转换为只读
·重新实现一个属性的设置和读取方法,可做边界判定
classMoney(object):
def__init__(self):
self.__money =0
@property
defmoney(self):
returnself.__money
@money.setter
defmoney(self, value):
ifisinstance(value, int):
self.__money = value
else:
print("error:不是整型数字")
运行结果
In [3]: a =Money()
In [4]:
In [4]:
In [4]: a.money
Out[4]:0
In [5]: a.money
=100
In [6]: a.money
Out[6]:100
1.4生成器
1.4.1什么是生成器
一边循环一边计算的机制,称为生成器:generator。
1.4.2创建生成器方法1
第一种方法很简单,只要把一个列表生成式的[ ]改成( )
In [15]: L = [ x*2forxinrange(5)]
In [16]: L
Out[16]: [0,2,4,6,8]
In [17]: G = ( x*2forxinrange(5))
In [18]: G
Out[18]: at0x7f626c132db0>
In [19]:
创建L和G的区别仅在于最外层的[ ]和( ),L是一个列表,而G是一个生成器。可以直接打印出L的每一个元素,但如果要一个一个打印出G的元素,可以通过next()函数获得生成器的下一个返回值:
In [19]: next(G)
Out[19]: 0
In [20]: next(G)
Out[20]: 2
In [21]: next(G)
Out[21]: 4
In [22]: next(G)
Out[22]: 6
In [23]: next(G)
Out[23]: 8
In [24]: next(G)
---------------------------------------------------------------------------
StopIterationTraceback (most recent call last)
in ()
----> 1 next(G)
StopIteration:
In [25]:
In [26]: G = ( x*2forxinrange(5))
In [27]:forxinG:
....:print(x)
....:
0
2
4
6
8
In [28]:
生成器保存的是算法,每次调用next(G),就计算出G的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的异常。当然,正确的方法是使用for循环,因为生成器也是可迭代对象。所以,创建了一个生成器后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration异常。
1.4.3创建生成器方法2
generator还可以用函数来实现。
比如,著名的斐波拉契数列(Fibonacci),除第一个和第二个数外,任意一个数都可由前两个数相加得到:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
斐波拉契数列用列表生成式写不出来,但是,用函数把它打印出来却很容易:
In [28]:deffib(times):
....:n =0
....:a,b =0,1
....:whilen
....:print(b)
....:a,b = b,a+b
....:n+=1
....:return'done'
....:
In [29]: fib(5)
1
1
2
3
5
Out[29]:'done'
可以看出,fib函数实际上是定义了斐波拉契数列的推算规则,可以从第一个元素开始,推算出后续任意的元素,这种逻辑其实非常类似generator。
上面的函数和generator仅一步之遥。要把fib函数变成generator,只需要把print(b)改为yield b就可以了:
In [30]: def fib(times):
....:n = 0
....:a,b = 0,1
....:while n
....:yield b
....:a,b = b,a+b
....:n+=1
....:return 'done'
....:
In [31]: F = fib(5)
In [32]: next(F)
Out[32]: 1
In [33]: next(F)
Out[33]: 1
In [34]: next(F)
Out[34]: 2
In [35]: next(F)
Out[35]: 3
In [36]: next(F)
Out[36]: 5
In [37]: next(F)
---------------------------------------------------------------------------
StopIterationTraceback (most recent call last)
in ()
----> 1 next(F)
StopIteration: done
上面fib的例子中,在循环过程中不断调用yield,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
In [38]:forninfib(5):
....:print(n)
....:
1
1
2
3
5
In [39]:
用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
In [39]: g = fib(5)
In [40]:whileTrue:
....:try:
....:x = next(g)
....:print("value:%d"%x)
....:exceptStopIterationase:
....:print("生成器返回值:%s"%e.value)
....:break
....:
value:1
value:1
value:2
value:3
value:5
生成器返回值:done
In [41]:
1.4.4send
例子:执行到yield时,gen函数作用暂时保存,返回i的值;temp接收下次c.send("python"),send发送过来的值,c.next()等价c.send(None)
In [10]:defgen():
....:i =0
....:whilei<5:
....:temp =yieldi
....:print(temp)
....:i+=1
....:
使用next函数
In [11]: f = gen()
In [12]: next(f)
Out[12]: 0
In [13]: next(f)
None
Out[13]: 1
In [14]: next(f)
None
Out[14]: 2
In [15]: next(f)
None
Out[15]: 3
In [16]: next(f)
None
Out[16]: 4
In [17]: next(f)
None
---------------------------------------------------------------------------
StopIterationTraceback (most recent call last)
in ()
----> 1 next(f)
StopIteration:
使用__next__()方法
In [18]: f = gen()
In [19]: f.__next__()
Out[19]: 0
In [20]: f.__next__()
None
Out[20]: 1
In [21]: f.__next__()
None
Out[21]: 2
In [22]: f.__next__()
None
Out[22]: 3
In [23]: f.__next__()
None
Out[23]: 4
In [24]: f.__next__()
None
---------------------------------------------------------------------------
StopIterationTraceback (most recent call last)
in ()
----> 1 f.__next__()
StopIteration:
使用send
In [43]: f = gen()
In [44]: f.__next__()
Out[44]:0
In [45]: f.send('haha')
haha
Out[45]:1
In [46]: f.__next__()
None
Out[46]:2
In [47]: f.send('haha')
haha
Out[47]:3
In [48]:
1.4.5实现多任务
模拟多任务实现方式之一:协程
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__()
总结
生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第n次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。
生成器的特点:
1.节约内存
2.迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的
1.5迭代器
迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
1.5.1可迭代对象
以直接作用于for循环的数据类型有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
1.5.2判断是否可以迭代
可以使用isinstance()判断一个对象是否是Iterable对象:
In [50]:fromcollectionsimportIterable
In [51]: isinstance([], Iterable)
Out[51]:True
In [52]: isinstance({}, Iterable)
Out[52]:True
In [53]: isinstance('abc', Iterable)
Out[53]:True
In [54]: isinstance((xforxinrange(10)), Iterable)
Out[54]:True
In [55]: isinstance(100, Iterable)
Out[55]:False
生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
1.5.3迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterator对象:
In [56]:fromcollectionsimportIterator
In [57]: isinstance((xforxinrange(10)), Iterator)
Out[57]:True
In [58]: isinstance([], Iterator)
Out[58]:False
In [59]: isinstance({}, Iterator)
Out[59]:False
In [60]: isinstance('abc', Iterator)
Out[60]:False
In [61]: isinstance(100, Iterator)
Out[61]:False
1.5.4iter()函数
生成器都是Iterator对象,但list、dict、str虽然是Iterable,却不是Iterator。
把list、dict、str等Iterable变成Iterator可以使用iter()函数:
In [62]: isinstance(iter([]), Iterator)
Out[62]:True
In [63]: isinstance(iter('abc'), Iterator)
Out[63]:True
总结
·凡是可作用于for循环的对象都是Iterable类型;
·凡是可作用于next()函数的对象都是Iterator类型
·集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
·目的是在使用集合的时候,减少占用的内容。
1.6闭包
1.6.1函数引用
deftest1():
print("--- in test1 func----")
#调用函数
test1()
#引用函数
ret = test1
print(id(ret))
print(id(test1))
#通过引用调用函数
ret()
运行结果:
---intest1 func----
140212571149040
140212571149040
---intest1 func----
1.6.2什么是闭包
#定义一个函数
deftest(number):
#在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
deftest_in(number_in):
print("in test_in函数, number_in is %d"%number_in)
returnnumber+number_in
#其实这里返回的就是闭包的结果
returntest_in
#给test函数赋值,这个20就是给参数number
ret = test(20)
#注意这里的100其实给参数number_in
print(ret(100))
#注意这里的200其实给参数number_in
print(ret(200))
运行结果:
intest_in函数, number_inis100
120
intest_in函数, number_inis200
220
1.6.3看一个闭包的实际例子:
defline_conf(a, b):
defline(x):
returna*x + b
returnline
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,就可以获得不同的直线表达函数。由此可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,就需要每次创建直线函数的时候同时说明a,b,x。这样,就需要更多的参数传递,也减少了代码的可移植性。
注意:
1.闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成
2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
1.7装饰器
装饰器,功能就是在运行原来功能基础上,加上一些其它功能,比如权限的验证,比如日志的记录等等。不修改原来的代码,进行功能的扩展。
1.7.1装饰器的理解
装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
1.7.2多个装饰器
#定义函数:完成包裹数据
defmakeBold(fn):
defwrapped():
return""+ fn() +""
returnwrapped
#定义函数:完成包裹数据
defmakeItalic(fn):
defwrapped():
return""+ fn() +""
returnwrapped
@makeBold
deftest1():
return"hello world-1"
@makeItalic
deftest2():
return"hello world-2"
@makeBold
@makeItalic
deftest3():
return"hello world-3"
print(test1()))
print(test2()))
print(test3()))
运行结果:
hello world-1
hello world-2
hello world-3
1.7.3装饰器(decorator)功能
1.引入日志
2.函数执行时间统计
3.执行函数前预备处理
4.执行函数后清理功能
5.权限校验等场景
6.缓存
1.7.4装饰器示例
1.7.4.1例1:无参数的函数
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc():
print("%s called at %s"%(func.__name__, ctime()))
func()
returnwrappedfunc
@timefun
deffoo():
print("I am foo")
foo()
sleep(2)
foo()
上面代码理解装饰器执行行为可理解成
foo = timefun(foo)
#foo先作为参数赋值给func后,foo接收指向timefun返回的wrappedfunc
foo()
#调用foo(),即等价调用wrappedfunc()
#内部函数wrappedfunc被引用,所以外部函数的func变量(自由变量)并没有释放
#func里保存的是原foo函数对象
1.7.4.2例2:被装饰的函数有参数
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc(a, b):
print("%s called at %s"%(func.__name__, ctime()))
print(a, b)
func(a, b)
returnwrappedfunc
@timefun
deffoo(a, b):
print(a+b)
foo(3,5)
sleep(2)
foo(2,4)
1.7.4.3例3:被装饰的函数有不定长参数
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc(*args, **kwargs):
print("%s called at %s"%(func.__name__, ctime()))
func(*args, **kwargs)
returnwrappedfunc
@timefun
deffoo(a, b, c):
print(a+b+c)
foo(3,5,7)
sleep(2)
foo(2,4,9)
1.7.4.4例4:装饰器中的return
fromtimeimportctime, sleep
deftimefun(func):
defwrappedfunc():
print("%s called at %s"%(func.__name__, ctime()))
func()
returnwrappedfunc
@timefun
deffoo():
print("I am foo")
@timefun
defgetInfo():
return'----hahah---'
foo()
sleep(2)
foo()
print(getInfo())
执行结果:
foo called at Fri Nov421:55:352016
I am foo
foo called at Fri Nov421:55:372016
I am foo
getInfo called at Fri Nov421:55:372016
None
如果修改装饰器为return
func(),则运行结果:
foo called at Fri Nov421:55:572016
I am foo
foo called at Fri Nov421:55:592016
I am foo
getInfo called at Fri Nov421:55:592016
----hahah---
总结:
·一般情况下为了让装饰器更通用,可以有return
1.7.4.5例5:装饰器带参数,在原有装饰器的基础上,设置外部变量
#decorator2.py
fromtimeimportctime, sleep
deftimefun_arg(pre="hello"):
deftimefun(func):
defwrappedfunc():
print("%s called at %s %s"%(func.__name__, ctime(), pre))
returnfunc()
returnwrappedfunc
returntimefun
@timefun_arg("wangcai")
deffoo():
print("I am foo")
@timefun_arg("python")
deftoo():
print("I am too")
foo()
sleep(2)
foo()
too()
sleep(2)
too()
可以理解为
foo()==timefun_arg("wangcai")(foo)()
1.7.4.6例6:类装饰器(扩展,非重点)
装饰器函数其实是这样一个接口约束,它必须接受一个callable对象作为参数,然后返回一个callable对象。在Python中一般callable对象都是函数,但也有例外。只要某个对象重写了__call__()方法,那么这个对象就是callable的。
classTest():
def__call__(self):
print('call me!')
t = Test()
t()# call me
类装饰器demo
classTest(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
deftest():
print("----test---")
test()
showpy()#如果把这句话注释,重新运行程序,依然会看到"--初始化--"
运行结果如下:
---初始化---
func nameistest
---装饰器中的功能---
----test---
1.8python是动态语言
1.8.1动态语言的定义
动态编程语言是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。
1.8.2运行的过程中给对象绑定(添加)属性
>>>classPerson(object):
def__init__(self, name = None, age = None):
self.name = name
self.age = age
>>>P = Person("小明","24")
>>>
上面定义了1个类Person,在这个类里,定义了两个初始属性name和age,
>>>P.sex ="male"
>>>P.sex
'male'
>>>
动态给实例绑定sex属性。
1.8.3运行的过程中给类绑定(添加)属性
>>>P1 = Person("小丽","25")
>>>P1.sex
Traceback (most recent call last):
File"", line1,in
P1.sex
AttributeError: Person instance has no attribute'sex'
>>>
尝试打印P1.sex,发现报错,P1没有sex这个属性。可以直接给Person绑定属性。
>>>> Person.sex =None#给类Person添加一个属性
>>>P1 = Person("小丽","25")
>>>print(P1.sex)#如果P1这个实例对象中没有sex属性的话,那么就会访问它的类属性
None#可以看到没有出现异常
>>>
1.8.4运行的过程中给类绑定(添加)方法
functio绑定:
>>>classPerson(object):
def__init__(self, name = None, age = None):
self.name = name
self.age = age
defeat(self):
print("eat food")
>>>defrun(self, speed):
print("%s在移动,速度是%d km/h"%(self.name, speed))
>>>P = Person("老王",24)
>>>P.eat()
eat food
>>>
>>>P.run()
Traceback (most recent call last):
File"", line1,in
P.run()
AttributeError: Person instance has no attribute'run'
>>>
>>>
>>>importtypes
>>>P.run = types.MethodType(run, P)
>>>P.run(180)
老王在移动,速度是180km/h
给对象添加一个方法是对象.方法名= xxxx
完整的代码如下:
importtypes
#定义了一个类
classPerson(object):
num =0
def__init__(self, name = None, age = None):
self.name = name
self.age = age
defeat(self):
print("eat food")
#定义一个实例方法
defrun(self, speed):
print("%s在移动,速度是%d km/h"%(self.name, speed))
#定义一个类方法
@classmethod
deftestClass(cls):
cls.num =100
#定义一个静态方法
@staticmethod
deftestStatic():
print("---static method----")
#创建一个实例对象
P = Person("老王",24)
#调用在class中的方法
P.eat()
#给这个对象添加实例方法
P.run = types.MethodType(run, P)
#调用实例方法
P.run(180)
#给Person类绑定类方法
Person.testClass = testClass
#调用类方法
print(Person.num)
Person.testClass()
print(Person.num)
#给Person类绑定静态方法
Person.testStatic = testStatic
#调用静态方法
Person.testStatic()
1.8.5运行的过程中删除属性、方法
删除的方法:
1.del对象.属性名
2.delattr(对象, "属性名")
1.8.6__slots__
动态语言与静态语言的不同:
动态语言:可以在运行的过程中,修改代码
静态语言:编译时已经确定好代码,运行过程中不能修改
Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
>>>classPerson(object):
__slots__ = ("name","age")
>>>P = Person()
>>>P.name ="老王"
>>>P.age =20
>>>P.score =100
Traceback (most recent call last):
File"", line1,in
AttributeError: Person instance has no attribute'score'
>>>
注意:
·使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
In [67]:classTest(Person):
...:pass
...:
In [68]: t = Test()
In [69]: t.score =100
1.9元类
1.9.1类也是对象
类就是一组用来描述如何生成一个对象的代码段:
>>>classObjectCreator(object):
…pass
…
>>>my_object = ObjectCreator()
>>>printmy_object
<__main__.ObjectCreator object at0x8974f2c>
类同样也是一种对象。
Python解释器在执行class的时候会创建一个对象。
下面的代码段:
>>>classObjectCreator(object):
…pass
…
将在内存中创建一个对象,名字就是ObjectCreator。这个对象(类对象ObjectCreator)拥有创建对象(实例对象)的能力。但是,它的本质仍然是一个对象,可以进行如下的操作:
1.可以将它赋值给一个变量
2.可以拷贝它
3.可以为它增加属性
4.可以将它作为函数参数进行传递
下面是示例:
>>> print ObjectCreator#可以打印一个类,因为它其实也是一个对象
>>> def echo(o):
…print o
…
>>> echo(ObjectCreator)#可以将类做为参数传给函数
>>> print hasattr(ObjectCreator, 'new_attribute')
Fasle
>>> ObjectCreator.new_attribute = 'foo' #可以为类增加属性
>>> print hasattr(ObjectCreator, 'new_attribute')
True
>>> print ObjectCreator.new_attribute
foo
>>> ObjectCreatorMirror = ObjectCreator #可以将类赋值给一个变量
>>> print ObjectCreatorMirror()
<__main__.ObjectCreator object at 0x8997b4c>
1.9.2动态地创建类
类也是对象,可以在运行时动态的创建它们,就像其他任何对象一样。
可以在函数中创建类,使用class关键字:
>>> def choose_class(name):
…if name == 'foo':
…class Foo(object):
…pass
…return Foo#返回的是类,不是类的实例
…else:
…class Bar(object):
…pass
…return Bar
…
>>> MyClass = choose_class('foo')
>>> print MyClass#函数返回的是类,不是类的实例
>>> print MyClass()#可以通过这个类创建类实例,也就是对象
<__main__.Foo object at 0x89c6d4c>
内建函数type查看对象的类型:
>>> print type(1) #数值的类型
>>> print type("1") #字符串的类型
>>> print type(ObjectCreator()) #实例对象的类型
>>> print type(ObjectCreator) #类的类型
1.9.3使用type创建类
type可以动态的创建类。
type可以接受一个类的描述作为参数,然后返回一个类。
type可以像这样工作:
type(类名,由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
比如下面的代码:
In [2]:classTest:#定义了一个Test类
...:pass
...:
In [3]: Test()#创建了一个Test类的实例对象
Out[3]: <__main__.Test at0x10d3f8438>
可以手动像这样创建:
Test2 = type("Test2",(),{})#定了一个Test2类
In [5]: Test2()#创建了一个Test2类的实例对象
Out[5]: <__main__.Test2 at0x10d406b38>
使用"Test2"作为类名,并且也可以把它当做一个变量来作为类的引用。类和变量是不同的,这里没有任何理由把事情弄的复杂。即type函数中第1个实参,也可以叫做其他的名字,这个名字表示类的名字
In [23]: MyDogClass = type('MyDog', (), {})
In [24]: print MyDogClass
使用help来测试这2个类
In [10]: help(Test) #用help查看Test类
Help on class Test in module __main__:
class Test(builtins.object)
|Data descriptors defined here:
|
|__dict__
|dictionary for instance variables (if defined)
|
|__weakref__
|list of weak references to the object (if defined)
In [8]: help(Test2) #用help查看Test2类
Help on class Test2 in module __main__:
class Test2(builtins.object)
|Data descriptors defined here:
|
|__dict__
|dictionary for instance variables (if defined)
|
|__weakref__
|list of weak references to the object (if defined)
1.9.4使用type创建带有属性的类
type接受一个字典来为类定义属性,因此
>>>Foo = type('Foo', (), {'bar':True})
可以翻译为:
>>>classFoo(object):
…bar =True
并且可以将Foo当成一个普通的类一样使用:
>>> print Foo
>>> print Foo.bar
True
>>> f = Foo()
>>> print f
<__main__.Foo object at 0x8a9b84c>
>>> print f.bar
True
可以向这个类继承,所以,如下的代码:
>>>classFooChild(Foo):
…pass
就可以写成:
>>> FooChild = type('FooChild', (Foo,),{})
>>> print FooChild
>>> print FooChild.bar# bar属性是由Foo继承而来
True
注意:
·type的第2个参数,元组中是父类的名字,而不是字符串
·添加的属性是类属性,并不是实例属性
1.9.5使用type创建带有方法的类
添加实例方法
In [46]:defecho_bar(self):#定义了一个普通的函数
...:print(self.bar)
...:
In [47]: FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})#让FooChild类中的echo_bar属性,指向了上面定义的函数
In [48]: hasattr(Foo,'echo_bar')#判断Foo类中,是否有echo_bar这个属性
Out[48]:False
In [49]:
In [49]: hasattr(FooChild,'echo_bar')#判断FooChild类中,是否有echo_bar这个属性
Out[49]:True
In [50]: my_foo = FooChild()
In [51]: my_foo.echo_bar()
True
添加静态方法
In [36]: @staticmethod
...:deftestStatic():
...:print("static method ....")
...:
In [37]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar,"testStatic":
...: testStatic})
In [38]: fooclid = Foochild()
In [39]: fooclid.testStatic
Out[39]:
In [40]: fooclid.testStatic()
static method ....
In [41]: fooclid.echo_bar()
True
添加类方法
In [42]: @classmethod
...:deftestClass(cls):
...:print(cls.bar)
...:
In [43]:
In [43]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar,"testStatic":
...: testStatic,"testClass":testClass})
In [44]:
In [44]: fooclid = Foochild()
In [45]: fooclid.testClass()
True
在Python中,类也是对象,可以动态的创建类。
1.9.6到底什么是元类
元类就是用来创建类的“东西”。
元类就是用来创建类(对象)的,元类就是类的类:
MyClass = MetaClass()#使用元类创建出一个对象,这个对象称为“类”
MyObject = MyClass()#使用“类”来创建出实例对象
元类就是创建类这种对象的东西。type就是Python的内建元类。
1.9.7__metaclass__属性
可以在定义一个类的时候添加__metaclass__属性。
classFoo(object):
__metaclass__ = something…
...省略...
Python会用元类来创建类Foo。先写下class Foo(object),但是类Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。如下代码:
classFoo(Bar):
pass
Python做了如下的操作:
1.Foo中有__metaclass__这个属性吗?如果是,Python会通过__metaclass__创建一个名字为Foo的类(对象)
2.如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。
3.如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。
4.如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。
1.9.8自定义元类
__metaclass__可以被任意调用,它并不需要是一个正式的类。
1.拦截类的创建
2.修改类
3.返回修改之后的类
1.10垃圾回收
1.10.1小整数对象池
整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。
Python对小整数的定义是[-5, 257)这些整数对象是提前建立好的,不会被垃圾回收。在一个Python的程序中,所有位于这个范围内的整数使用的都是同一个对象.
同理,单个字母也是这样的。
但是当定义2个相同的字符串时,引用计数为0,触发垃圾回收
1.10.2大整数对象池
每一个大整数,均创建一个新的对象。
1.10.3intern机制
·小整数[-5,257)共用对象,常驻内存
·单个字符共用对象,常驻内存
·单个单词,不可修改,默认开启intern机制,共用对象,引用计数为0,则销毁
字符串(含有空格),不可修改,没开启intern机制,不共用对象,引用计数为0,销毁
大整数不共用内存,引用计数为0,销毁
数值类型和字符串类型在Python中都是不可变的,这意味着每次对变量的修改实际上是创建一个新的对象
1.10.4Garbage collection(GC垃圾回收)
python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
引用计数机制:
python里每一个东西都是对象,它们的核心就是一个结构体:PyObject
typedefstruct_object {
intob_refcnt;
struct_typeobject *ob_type;
} PyObject;
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少
#definePy_INCREF(op)((op)->ob_refcnt++)//增加计数
#definePy_DECREF(op) \//减少计数
if(--(op)->ob_refcnt !=0) \
; \
else\
__Py_Dealloc((PyObject *)(op))
当引用计数为0时,该对象生命就结束了。
引用计数机制的优点:
·简单
·实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处:处理回收内存的时间分摊到了平时。
引用计数机制的缺点:
·维护引用计数消耗资源
·循环引用
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的。对于如今的强大硬件,缺点1尚可接受,但是循环引用导致内存泄露,注定python还将引入新的回收机制。(标记清除和分代收集)
1.10.5gc模块
1.10.5.1垃圾回收机制
Python中的垃圾回收是以引用计数为主,分代收集为辅。
1.10.5.1.1导致引用计数+1的情况
·对象被创建,例如a=23
·对象被引用,例如b=a
·对象被作为参数,传入到一个函数中,例如func(a)
·对象作为一个元素,存储在容器中,例如list1=[a,a]
1.10.5.1.2导致引用计数-1的情况
·对象的别名被显式销毁,例如del a
·对象的别名被赋予新的对象,例如a=24
·一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
·对象所在的容器被销毁,或从容器中删除对象
1.10.5.1.3查看一个对象的引用计数
importsys
a ="hello world"
sys.getrefcount(a)
可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1
1.10.5.2循环引用导致内存泄露
引用计数的缺陷是循环引用的问题
importgc
classClassA():
def__init__(self):
print('object born,id:%s'%str(hex(id(self))))
deff2():
whileTrue:
c1 = ClassA()
c2 = ClassA()
c1.t = c2
c2.t = c1
delc1
delc2
#把python的gc关闭
gc.disable()
f2()
执行f2(),进程占用的内存会不断增大。
·创建了c1,c2后这两块内存的引用计数都是1,执行c1.t=c2和c2.t=c1后,这两块内存的引用计数变成2.
·在del c1后,内存1的对象的引用计数变为1,由于不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依然是2,在del c2后,同理,内存1的对象,内存2的对象的引用数都是1。
·虽然它们两个的对象都是可以被销毁的,但是由于循环引用,导致垃圾回收器都不会回收它们,所以就会导致内存泄露。
1.10.5.3垃圾回收
#coding=utf-8
importgc
classClassA():
def__init__(self):
print('object born,id:%s'%str(hex(id(self))))
# def __del__(self):
#print('object del,id:%s'%str(hex(id(self))))
deff3():
print("-----0------")
# print(gc.collect())
c1 = ClassA()
c2 = ClassA()
c1.t = c2
c2.t = c1
print("-----1------")
delc1
delc2
print("-----2------")
print(gc.garbage)
print("-----3------")
print(gc.collect())#显式执行垃圾回收
print("-----4------")
print(gc.garbage)
print("-----5------")
if__name__ =='__main__':
gc.set_debug(gc.DEBUG_LEAK)#设置gc模块的日志
f3()
python2运行结果:
-----0------
object born,id:0x724b20
object born,id:0x724b48
-----1------
-----2------
[]
-----3------
gc: collectable
gc: collectable
gc: collectable
gc: collectable
4
-----4------
[<__main__.ClassA instance at0x724b20>, <__main__.ClassA instance at0x724b48>, {'t': <__main__.ClassA instance at0x724b48>}, {'t': <__main__.ClassA instance at0x724b20>}]
-----5------
说明:
·垃圾回收后的对象会放在gc.garbage列表里面
·gc.collect()会返回不可达的对象数目,4等于两个对象以及它们对应的dict
有三种情况会触发垃圾回收:
1.调用gc.collect(),
2.当gc模块的计数器达到阀值的时候。
3.程序退出的时候
1.10.5.4gc模块常用功能解析
gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到,采用引用计数的方法管理内存的一个缺陷是循环引用,而gc模块的一个主要功能就是解决循环引用的问题。
1.10.5.4.1常用函数:
1、gc.set_debug(flags)设置gc的debug日志,一般设置为gc.DEBUG_LEAK
2、gc.collect([generation])显式进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一,二代的对象,2代表检查一,二,三代的对象,如果不传参数,执行一个full collection,也就是等于传2。返回不可达(unreachable
objects)对象的数目
3、gc.get_threshold()获取的gc模块中自动执行垃圾回收的频率。
4、gc.set_threshold(threshold0[,
threshold1[, threshold2])设置自动执行垃圾回收的频率。
5、gc.get_count()获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表
1.10.5.4.2gc模块的自动垃圾回收机制
必须要import gc模块,并且is_enable()=True才会启动自动垃圾回收。
这个机制的主要作用就是发现并处理不可达的垃圾对象。
垃圾回收=垃圾检查+垃圾回收
在Python中,采用分代收集的方法。把对象分为三代,一开始,对象在创建的时候,放在一代中,如果在一次一代的垃圾检查中,改对象存活下来,就会被放到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中。
gc模块里面会有一个长度为3的列表的计数器,可以通过gc.get_count()获取。
例如(488,3,0),其中488是指距离上一次一代垃圾检查,Python分配内存的数目减去释放内存的数目,注意是内存分配,而不是引用计数的增加。例如:
printgc.get_count()# (590, 8, 0)
a = ClassA()
printgc.get_count()# (591, 8, 0)
dela
printgc.get_count()# (590, 8, 0)
3是指距离上一次二代垃圾检查,一代垃圾检查的次数,同理,0是指距离上一次三代垃圾检查,二代垃圾检查的次数。
gc模快有一个自动垃圾回收的阀值,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10)每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器
例如,假设阀值是(700,10,10):
当计数器从(699,3,0)增加到(700,3,0),gc模块就会执行gc.collect(0),即检查一代对象的垃圾,并重置计数器为(0,4,0)
当计数器从(699,9,0)增加到(700,9,0),gc模块就会执行gc.collect(1),即检查一、二代对象的垃圾,并重置计数器为(0,0,1)
当计数器从(699,9,9)增加到(700,9,9),gc模块就会执行gc.collect(2),即检查一、二、三代对象的垃圾,并重置计数器为(0,0,0)
注意点
gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法
importgc
classClassA():
pass
# def __del__(self):
#print('object born,id:%s'%str(hex(id(self))))
gc.set_debug(gc.DEBUG_LEAK)
a = ClassA()
b = ClassA()
a.next = b
b.prev = a
print"--1--"
printgc.collect()
print"--2--"
dela
print"--3--"
delb
print"--3-1--"
printgc.collect()
print"--4--"
运行结果:
--1--
0
--2--
--3--
--3-1--
gc: collectable
gc: collectable
gc: collectable
gc: collectable
4
--4--
如果把del打开,运行结果为:
--1--
0
--2--
--3--
--3-1--
gc: uncollectable
gc: uncollectable
gc: uncollectable
gc: uncollectable
4
--4--
1.11内建属性
子类没有实现__init__方法时,默认自动调用父类的。如定义__init__方法时,需自己手动调用父类的__init__方法
__getattribute__例子:
classTest(object):
def__init__(self,subject1):
self.subject1 = subject1
self.subject2 ='cpp'
#属性访问时拦截器,打log
def__getattribute__(self,obj):
ifobj =='subject1':
print('log subject1')
return'redirect python'
else:#测试时注释掉这2行,将找不到subject2
returnobject.__getattribute__(self,obj)
defshow(self):
print('this is Test')
s = Test("python")
print(s.subject1)
print(s.subject2)
运行结果:
log subject1
redirect python
cpp
__getattribute__的坑
classPerson(object):
def__getattribute__(self,obj):
print("---test---")
ifobj.startswith("a"):
return"hahha"
else:
returnself.test
deftest(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
1.12内建函数
Build-in Function,启动python解释器,输入dir(__builtins__),可以看到很多python解释器启动后默认加载的属性和函数,这些函数称之为内建函数,这些函数因为在编程时使用较多,cpython解释器用c语言实现了这些函数,启动解释器时默认加载。
1.12.1range
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers
·start:计数从start开始。默认是从0开始。例如range(5)等价于range(0,5);
·stop:到stop结束,但不包括stop.例如:range(0,5)是[0,
1, 2, 3, 4]没有5
·step:每次跳跃的间距,默认为1。例如:range(0,5)等价于range(0, 5, 1)
range返回一个迭代值。如果想得到列表,可通过list函数
a = range(5)
list(a)
创建列表的另外一种方法
In [21]: testList = [x+2forxinrange(5)]
In [22]: testList
Out[22]: [2,3,4,5,6]
1.12.2map函数
map函数会根据提供的函数对指定序列做映射
map(...)
map(function, sequence[, sequence, ...]) -> list
·function:是一个函数
·sequence:是一个或多个序列,取决于function需要几个参数
·返回值是一个map
参数序列中的每一个元素分别调用function函数,返回包含每次function函数返回值的list。
#函数需要一个参数
map(lambdax: x*x, [1,2,3])
#结果为:[1, 4, 9]
#函数需要两个参数
map(lambdax, y: x+y, [1,2,3], [4,5,6])
#结果为:[5, 7, 9]
deff1( x, y ):
return(x,y)
l1 = [0,1,2,3,4,5,6]
l2 = ['Sun','M','T','W','T','F','S']
l3 = map( f1, l1, l2 )
print(list(l3))
#结果为:[(0, 'Sun'), (1, 'M'), (2, 'T'), (3, 'W'), (4, 'T'), (5, 'F'), (6, 'S')]
1.12.3filter函数
filter函数会对指定序列执行过滤操作
filter(...)
filter(function or None, sequence) -> list, tuple, or string
Return those items of sequence for which function(item) is true.If
function is None, return the items that are true.If sequence is a tuple
or string, return the same type, else return a list.
·function:接受一个参数,返回布尔值True或False
·sequence:序列可以是str,tuple,list
filter函数会对序列参数sequence中的每个元素调用function函数,最后返回的结果包含调用结果为True的元素。
返回值的类型和参数sequence的类型相同
filter(lambdax: x%2, [1,2,3,4])
[1,3]
filter(None,"she")
'she'
1.12.4reduce函数
reduce函数,reduce函数会对参数序列中元素进行累积
reduce(...)
reduce(function, sequence[, initial]) -> value
Apply a function of two arguments cumulatively to the items of a sequence,
from left to right, so as to reduce the sequence to a single value.
For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
((((1+2)+3)+4)+5).If initial is present, it is placed before the items
of the sequence in the calculation, and serves as a default when the
sequence is empty.
·function:该函数有两个参数
·sequence:序列可以是str,tuple,list
·initial:固定初始值
reduce依次从sequence中取一个元素,和上一次调用function的结果做参数再次调用function。第一次调用function时,如果提供initial参数,会以sequence中的第一个元素和initial作为参数调用function,否则会以序列sequence中的前两个元素做参数调用function。注意function函数不能为None。
reduce(lambdax, y: x+y, [1,2,3,4])
10
reduce(lambdax, y: x+y, [1,2,3,4],5)
15
reduce(lambdax, y: x+y, ['aa','bb','cc'],'dd')
'ddaabbcc'
在Python3里,reduce函数已经被从全局名字空间里移除了,它现在被放置在fucntools模块里用的话要先引入:from functools import
reduce
1.12.5sorted函数
sorted(...)
sorted(iterable, key=None, reverse=False) --> new sorted list
1.13functools
一些工具函数放在此functools里。
importfunctools
dir(functools)
运行结果:
['MappingProxyType',
'RLock',
'WRAPPER_ASSIGNMENTS',
'WRAPPER_UPDATES',
'WeakKeyDictionary',
'_CacheInfo',
'_HashedSeq',
'__all__',
'__builtins__',
'__cached__',
'__doc__',
'__file__',
'__loader__',
'__name__',
'__package__',
'__spec__',
'_c3_merge',
'_c3_mro',
'_compose_mro',
'_convert',
'_find_impl',
'_ge_from_gt',
'_ge_from_le',
'_ge_from_lt',
'_gt_from_ge',
'_gt_from_le',
'_gt_from_lt',
'_le_from_ge',
'_le_from_gt',
'_le_from_lt',
'_lru_cache_wrapper',
'_lt_from_ge',
'_lt_from_gt',
'_lt_from_le',
'_make_key',
'cmp_to_key',
'get_cache_token',
'lru_cache',
'namedtuple',
'partial',
'partialmethod',
'reduce',
'singledispatch',
'total_ordering',
'update_wrapper',
'wraps']
1.13.1partial函数(偏函数)
把一个函数的某些参数设置默认值,返回一个新的函数,调用这个新函数会更简单。
importfunctools
defshowarg(*args, **kw):
print(args)
print(kw)
p1=functools.partial(showarg,1,2,3)
p1()
p1(4,5,6)
p1(a='python', b='test')
p2=functools.partial(showarg, a=3,b='linux')
p2()
p2(1,2)
p2(a='python', b='test')
1.13.2wraps函数
使用装饰器时,有一些细节需要被注意。例如,被装饰后的函数其实已经是另外一个函数了(函数名等函数属性会发生改变)。
添加后由于函数名和函数的doc发生了改变,对测试结果有一些影响,例如:
defnote(func):
"note function"
defwrapper():
"wrapper function"
print('note something')
returnfunc()
returnwrapper
@note
deftest():
"test function"
print('I am test')
test()
print(test.__doc__)
运行结果
note something
I am test
wrapper function
所以,Python的functools包中提供了一个叫wraps的装饰器来消除这样的副作用。例如:
importfunctools
defnote(func):
"note function"
@functools.wraps(func)
defwrapper():
"wrapper function"
print('note something')
returnfunc()
returnwrapper
@note
deftest():
"test function"
print('I am test')
test()
print(test.__doc__)
运行结果
note something
I am test
test function
1.14模块进阶
Python有一套很有用的标准库(standard library)。标准库会随着Python解释器一起安装。它是Python的一个组成部分。
1.14.1常用标准库
1.14.1.1time
1、time有2种时间表示形式:
1、时间戳表示法,即以整型或浮点型表示的是一个以秒为单位的时间间隔。这个时间的基础值是从1970年的1月1号零点开始算起。
2、元组格式表示法,即一种Python的数据结构表示。这个元组有9个整型内容。分别表示不同的时间含义。
2、名词解释:
UTC(Coordinated Universal Time,世界协调时)亦即格林威治天文时间,世界标准时间。在中国为UTC+8。
DST(Daylight Saving Time)即夏令时。是一种为节约能源而人为规定地方时间的制度,一般在天亮早的夏季人为将时间提前一小时。
3、包含的变量:
timezone --当地时间与标准UTC时间的误差,以秒计
altzone --当地夏令时时间与标准UTC时间的误差,以秒计
daylight --当地时间是否反映夏令时,默认为0
zname --关于(标准时区名称,夏令时时区名称)的元组
4、包含的函数:
time() --返回当前时间戳,浮点数形式。不接受参数
clock() --返回当前程序的cpu执行时间。unix系统始终返回全部运行时间;而windows从第二次开始都是以第一次调用此函数时的时间戳作为基准,而不是程序开始时间为基准。不接受参数。
sleep() --延迟一个时间段,接受整型、浮点型。
gmtime() --将时间戳转换为UTC时间元组格式。接受一个浮点型时间戳参数,其默认值为当前时间戳。
localtime()
--将时间戳转换为本地时间元组格式。接受一个浮点型时间戳参数,其默认值为当前时间戳。
asctime() --将时间元组格式转换为字符串形式。接受一个时间元组,其默认值为localtime()返回值
ctime() --将时间戳转换为字符串。接受一个时间戳,其默认值为当前时间戳。等价于asctime(localtime(seconds))
mktime() --将本地时间元组转换为时间戳。接受一个时间元组,必选。
strftime() --将时间元组以指定的格式转换为字符串形式。接受字符串格式化串、时间元组。时间元组为可选,默认为localtime()
strptime() --将指定格式的时间字符串解析为时间元组,strftime()的逆向过程。接受字符串,时间格式2个参数,都是必选。
tzset() --改变本地时区。
5、时间字符串支持的格式符号:
格式含义备注
%a本地(locale)简化星期名称
%A本地完整星期名称
%b本地简化月份名称
%B本地完整月份名称
%c本地相应的日期和时间表示
%d一个月中的第几天(01 - 31)
%H一天中的第几个小时(24小时制,00 - 23)
%I第几个小时(12小时制,01 - 12)
%j一年中的第几天(001 - 366)
%m月份(01 - 12)
%M分钟数(00 - 59)
%p本地am或者pm的相应符
%S秒(01 - 61)
%U一年中的星期数。(00 - 53星期天是一个星期的开始。)第一个星期天之前的所有天数都放在第0周。
%w一个星期中的第几天(0 - 6,0是星期天)
%W和%U基本相同,不同的是%W以星期一为一个星期的开始。
%x本地相应日期
%X本地相应时间
%y去掉世纪的年份(00 - 99)
%Y完整的年份
%Z时区的名字(如果不存在为空字符)
%%‘%’字符
1.14.1.2hashlib
importhashlib
m = hashlib.md5()#创建hash对象,md5:(message-Digest Algorithm 5)消息摘要算法,得出一个128位的密文
printm#
m.update('test')#更新哈希对象以字符串参数
printm.hexdigest()#返回十六进制数字字符串
应用实例
用于注册、登录....
importhashlib
importdatetime
KEY_VALUE ='test'
now = datetime.datetime.now()
m = hashlib.md5()
str ='%s%s'% (KEY_VALUE,now.strftime("%Y%m%d"))
m.update(str.encode('utf-8'))
value = m.hexdigest()
print(value)
运行结果:
8ad2d682e3529dac50e586fee8dc05c0
1.14.2常用扩展库
1.14.2.1SimpleHTTPServer
可以运行静态服务。
在终端中输入命令:
python -m http.server PORT
1.15编码风格
错误认知
·这很浪费时间
·我是个艺术家
·所有人都能穿的鞋不会合任何人的脚
·我善长制定编码规范
正确认知
·促进团队合作
·减少bug处理
·提高可读性,降低维护成本
·有助于代码审查
·养成习惯,有助于程序员自身的成长
pep8编码规范
Python Enhancement Proposals:python改进方案
Guido的关键点之一是:代码更多是用来读而不是写。编码规范旨在改善Python代码的可读性。
风格指南强调一致性。项目、模块或函数保持一致都很重要。
1.15.1每级缩进用4个空格。
括号中使用垂直隐式缩进或使用悬挂缩进。后者应该注意第一行要没有参数,后续行要有缩进。
·Yes
#对准左括号
foo = long_function_name(var_one, var_two,
var_three, var_four)
#不对准左括号,但加多一层缩进,以和后面内容区别。
deflong_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
#悬挂缩进必须加多一层缩进.
foo = long_function_name(
var_one, var_two,
var_three, var_four)
·No
#不使用垂直对齐时,第一行不能有参数。
foo = long_function_name(var_one, var_two,
var_three, var_four)
#参数的缩进和后续内容缩进不能区别。
deflong_function_name(
var_one, var_two, var_three,
var_four):
print(var_one)
4个空格的规则是对续行可选的。
#悬挂缩进不一定是4个空格
foo = long_function_name(
var_one, var_two,
var_three, var_four)
if语句跨行时,两个字符关键字(比如if)加上一个空格,再加上左括号构成了很好的缩进。后续行暂时没有规定,至少有如下三种格式,建议使用第3种。
#没有额外缩进,不是很好看,个人不推荐.
if(this_is_one_thingand
that_is_another_thing):
do_something()
#添加注释
if(this_is_one_thingand
that_is_another_thing):
# Since both conditions are true, we can frobnicate.
do_something()
#额外添加缩进,推荐。
# Add some extra indentation on the conditional continuation line.
if(this_is_one_thing
andthat_is_another_thing):
do_something()
1.15.2右边括号也可以另起一行。有两种格式,建议第2种。
#右括号不回退,个人不推荐
my_list = [
1,2,3,
4,5,6,
]
result = some_function_that_takes_arguments(
'a','b','c',
'd','e','f',
)
#右括号回退
my_list = [
1,2,3,
4,5,6,
]
result = some_function_that_takes_arguments(
'a','b','c',
'd','e','f',
)
1.15.3空格或Tab?
·空格是首选的缩进方法。
·Tab仅仅在已经使用tab缩进的代码中为了保持一致性而使用。
1.15.4最大行宽
·限制所有行的最大行宽为79字符。
·文本长块,比如文档字符串或注释,行长度应限制为72个字符。
1.15.5空行
·两行空行分割顶层函数和类的定义。
·类的方法定义用单个空行分割。
·额外的空行可以必要的时候用于分割不同的函数组,但是要尽量节约使用。
·额外的空行可以必要的时候在函数中用于分割不同的逻辑块,但是要尽量节约使用。
1.15.6源文件编码
·在核心Python发布的代码应该总是使用UTF-8。
·Python 3(默认UTF-8)不应有编码声明。
1.15.7导入在单独行
·Yes:
importos
importsys
fromsubprocessimportPopen, PIPE
·No:
importsys, os
·导入始终在文件的顶部,在模块注释和文档字符串之后,在模块全局变量和常量之前。
·导入顺序如下:标准库进口,相关的第三方库,本地库。各组的导入之间要有空行。
1.15.8禁止使用通配符导入。
通配符导入(fromimport*)应该避免,因为它不清楚命名空间有哪些名称存,混淆读者和许多自动化的工具。
1.15.9字符串引用
·Python中单引号字符串和双引号字符串都是相同的。注意尽量避免在字符串中的反斜杠以提高可读性。
·根据PEP 257,三个引号都使用双引号。
1.15.10括号里边避免空格
#括号里边避免空格
# Yes
spam(ham[1], {eggs:2})
# No
spam( ham[1], { eggs:2} )
1.15.11逗号,冒号,分号之前避免空格
#逗号,冒号,分号之前避免空格
# Yes
ifx ==4:printx, y; x, y = y, x
# No
ifx ==4:printx , y ; x , y = y , x
1.15.12索引操作中的冒号
当作操作符处理前后要有同样的空格(一个空格或者没有空格,个人建议是没有。
# Yes
ham[1:9], ham[1:9:3], ham[:9:3], ham[1::3], ham[1:9:]
ham[lower:upper], ham[lower:upper:], ham[lower::step]
ham[lower+offset : upper+offset]
ham[: upper_fn(x) : step_fn(x)], ham[:: step_fn(x)]
ham[lower + offset : upper + offset]
# No
ham[lower + offset:upper + offset]
ham[1:9], ham[1:9], ham[1:9:3]
ham[lower : : upper]
ham[ : upper]
1.15.13函数调用的左括号之前不能有空格
# Yes
spam(1)
dct['key'] = lst[index]
# No
spam (1)
dct ['key'] = lst [index]
1.15.14赋值等操作符前后
赋值等操作符前后不能因为对齐而添加多个空格
# Yes
x =1
y =2
long_variable =3
# No
x=1
y=2
long_variable =3
1.15.15二元运算符两边放置一个空格
涉及=、符合操作符( += , -=等)、比较( == , < , > , != , <> , <= , >= , in , not in , is ,
is not )、布尔( and , or , not )。
优先级高的运算符或操作符的前后不建议有空格。
# Yes
i = i +1
submitted +=1
x = x*2-1
hypot2 = x*x + y*y
c = (a+b) * (a-b)
# No
i=i+1
submitted +=1
x = x *2-1
hypot2 = x * x + y * y
c = (a + b) * (a - b)
1.15.16关键字参数和默认值参数的前后不要加空格
# Yes
defcomplex(real, imag=0.0):
returnmagic(r=real, i=imag)
# No
defcomplex(real, imag =0.0):
returnmagic(r = real, i = imag)
1.15.17通常不推荐复合语句
Compound statements:多条语句写在同一行
# Yes
iffoo =='blah':
do_blah_thing()
do_one()
do_two()
do_three()
# No
iffoo =='blah': do_blah_thing()
do_one(); do_two(); do_three()
尽管有时可以在if/for/while的同一行跟一小段代码,但绝不要跟多个子句,并尽量避免换行。
# No
iffoo =='blah': do_blah_thing()
forxinlst: total += x
whilet <10: t = delay()
更不是:
# No
iffoo =='blah': do_blah_thing()
else: do_non_blah_thing()
try: something()
finally: cleanup()
do_one(); do_two(); do_three(long, argument,
list, like, this)
iffoo =='blah': one(); two(); three()
1.15.18避免采用的名字
决不要用字符'l'(小写字母el),'O'(大写字母oh),或'I'(大写字母eye)作为单个字符的变量名。一些字体中,这些字符不能与数字1和0区别。用'L'代替'l'时。
1.15.19包和模块名
模块名要简短,全部用小写字母,可使用下划线以提高可读性。包名和模块名类似,但不推荐使用下划线。
1.16代码调试
1.16.1.1pycharm
步骤:
1、设置断点
2、shift+f9开始调试
3、光标就在断点处停了。这一行没有运行的
4、下一行:f8
5、进入方法:f7
6、跳到下一个断点:alt+f9
7、进入方法,跳出这一步,shift+f8