python 是门动态语言
class Person(object):
num = 0
def __init__(self, name=None, age = None):
self.name = name
self.age = age
p = Person('xm', '24')
p.name
'xm'
p.sex = 'male'
p.sex
'male'
Person.sex = None
p2 = Person('xl', 24)
print(p2.sex)
None
# 运行过程中给类绑定方法
def eat(self):
print('eat food')
import types
Person.eat = types.MethodType(eat, Person)
p.eat()
eat food
# 定义一个类方法
@classmethod
def t1(cls):
cls.num = 100
# 添加
Person.t1 = t1
# 调用
Person.t1()
print(Person.num)
100
# 定义一个静态方法
@staticmethod
def t2():
print('--------static method--------')
# 绑定
Person.t2 = t2
# 调用
Person.t2()
--------static method--------
运行的过程中删除属性方法
del Person.t2
Person.t2()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-123-f8efff5b70b3> in <module>()
----> 1 Person.t2()
AttributeError: type object 'Person' has no attribute 't2'
玩动态语言的时候小心动态的坑
如何避免,使用’slots’
# 如果我们想要限制实例的属性怎么办?比如,只允许对Person实例添加name和age属性。
class Person(object):
__slots__ = ('name', 'age')
p = Person()
p.name = 'hh'
p.age = 20
p.score = 100
#使用__slots__要注意,
#__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-126-91c47bf50c73> in <module>()
----> 1 p.score = 100
AttributeError: 'Person' object has no attribute 'score'
‘’
1.生成器
# 1
l = [x * 2 for x in range(5)]
l
[0, 2, 4, 6, 8]
g = (x * 2 for x in range(5))
g
<generator object <genexpr> at 0x10298f8b8>
for i in g:
print(i)
0
2
4
6
8
# 上面的生成器被使用过,无法再用?
g = (x * 2 for x in range(5))
next(g)
0
next(g)
2
next(g)
4
next(g)
6
next(g)
8
next(g)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-138-e734f8aca5ac> in <module>()
----> 1 next(g)
StopIteration:
生成器保存的是算法
每次调用 next(G) ,就计算出 G 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的异常。当然,这种不断调用 next() 实在是太变态了,正确的方法是使用 for 循环,因为生成器也是可迭代对象。所以,我们创建了一个生成器后,基本上永远不会调用 next() ,而是通过 for 循环来迭代它,并且不需要关心 StopIteration 异常。
# 方法2
# 用函数
# 此处的例子使用斐波那契数列
def fib(times):
n = 0
a,b = 0,1
while n < times:
print(b)
a,b = b,a+b
n += 1
return 'done'
fib(5)
1
1
2
3
5
'done'
def fib(times):
n = 0
a,b = 0,1
while n < times:
yield b
a,b = b,a+b
n += 1
return 'done'
F = fib(5)
for i in fib(5):
print(i)
1
1
2
3
5
yield 性质探究
程序执行到yield时就会停止
知道再次调用这个函数的时候,会重新在yield下一步开始
def test():
i = 0
while i < 5:
temp = yield i
print(temp)
i += 1
t = test()
# t为一个生成器
t.__next__()
0
t.__next__()
None
1
t.__next__()
None
2
t.send('hh') # send把'hh'传给了test中得temp
hh
3
next(t)
None
4
# 生成器的应用
def test1():
while True:
print('--1--')
yield None
def test2():
while True:
print('--2--')
yield None
#t1 = test1()
#t1 = test2()
# while True:
# t1.__next__()
# t2.__next__()
# 多任务: 看上去同时执行的程序就是多任务,切换足够快
# 这种事三种多任务方式的一种: 协程
2.迭代器
1> 可迭代对象
以直接作用于 for 循环的数据类型有以下几种:
一类是集合数据类型,如 list 、 tuple 、 dict 、 set 、 str 等;
一类是 generator ,包括生成器和带 yield 的generator function。
这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。
2> 判断是否可以迭代
from collections import Iterable
isinstance([], Iterable)
True
isinstance((), Iterable)
True
isinstance((x for x in range(10)), Iterable)
True
isinstance(100, Iterable)
False
3> 迭代器
# 可以被next()调用并不断返回下一个值的对象称为 迭代器 : Iterator
# 可以使用 isinstance() 判断一个对象是否是 Iterator 对象:
from collections import Iterator
isinstance([], Iterator)# 可迭代对象并不是一个迭代器
/Users/goodbyekiller/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:4: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
after removing the cwd from sys.path.
False
isinstance((x for x in range(10)), Iterator)
True
4> iter() 函数
生成器都是 Iterator 对象
但 list 、 dict 、 str 虽然是 Iterable ,却不是 Iterator 。
把 list 、 dict 、 str 等 Iterable 变成 Iterator 可以使用 iter() 函数
isinstance(iter([]), Iterator)
True
3.闭包
def test1():
print("--- in test1 func----")
ret = test1 # 变量ret存储指向函数test1的指针
print(id(test1))
print(id(ret))
4338532424
4338532424
# 什么是闭包
def test(n):
# 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量
# 那么将这个函数以及用到的一些变量称之为闭包
def test_in(n_in):
print(f'在 test_in 函数中, n_in 是{n_in}')
return n + n_in
# 返回闭包的结果
return test_in
ret = test(20)
print(ret(100))
print(' ')
print(test(20)(100))
print(' ')
print(test(20,100))
在 test_in 函数中, n_in 是100
120
在 test_in 函数中, n_in 是100
120
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-170-84387b2ca350> in <module>()
15 print(test(20)(100))
16 print(' ')
---> 17 print(test(20,100))
TypeError: test() takes 1 positional argument but 2 were given
内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。
nonlocal访问外部函数的局部变量(python3)
def counter(start=0):
def incr():
nonlocal start
start += 1
return start
return incr
c1 = counter(5)
print(c1())
print(c1())
6
7
# 一个闭包应用的实际例子
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,就可以获得不同的直线表达函数。
#由此,我们可以看到,闭包也具有提高代码可复用性的作用。
#如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。
#这样,我们就需要更多的参数传递,也减少了代码的可移植性。
#由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
3.装饰器
#引入
def t1():
print('-1-')
def t1():
print('-2-')
t1() # python 编译器从上到下顺序执行, t1在后面已经被修改, 调用的时候调用修改后的
-2-
# 在例如
def foo():
print('foo')
foo = lambda x: x + 2
foo(2) # foo不再保存之前的操作,转向新的匿名函数保存的操作
4
def w1(func):
def inner():
# 验证1
# 验证2
func()
return inner
@w1
def f1():
print('f1')
#
# 过程
# 执行w1函数 ,并将 @w1 下面的函数作为w1函数的参数,
# 即:@w1 等价于 w1(f1) 所以,内部就会去执行:
def inner():
#验证 1
#验证 2
#验证 3
f1() # func是参数,此时 func 等于 f1
return inner
# 返回的 inner,inner代表的是函数w1的内部函数,用于处理f1
# 其实就是将原来的 f1 函数塞进另外一个函数中
@函数名 是python的一种语法糖。
# 例子
def make_bold(fn):
def wrapped():
return '<b>' + fn() + '</b>'
return wrapped
def makeItalic(fn):
def wrapped():
return "<i>" + fn() + "</i>"
return wrapped
@make_bold
def t1():
return 'hello world!'
# 过程 将t1函数作为参数传给函数make_blod
# 内部函数调用 函数t1 ,及t1()
# 在函数make_blod中返回函数 wrapped , 而不是调用
# 装饰器返回的是一个函数,而不是返回一个函数的调用
print(t1())
# 而我们调用这个函数的时候, 及 t1()
# 会执行刚才 装饰t1的过程, 结果是返回内部的函数
# 并且调用这个返回的函数, 得到的结果即 最后结果
@make_bold
@makeItalic
def test3():
return "hello world-3" # 由内向外运行
print(test3())
<b>hello world!</b>
<b><i>hello world-3</i></b>
装饰器作用示例
1,无参数的函数
from time import ctime, sleep
def timefun(func):
def wrappedfunc():
print(f'{func.__name__} called at {ctime()}')
func()
return wrappedfunc
@timefun
def foo():
print('I am foo')
foo()
sleep(3)
foo()
foo called at Wed Jun 19 14:55:35 2019
I am foo
foo called at Wed Jun 19 14:55:38 2019
I am foo
foo = timefun(foo)
#foo先作为参数赋值给func后,foo接收指向timefun返回的wrappedfunc
foo()
#调用foo(),即等价调用wrappedfunc()
#内部函数wrappedfunc被引用,所以外部函数的foo变量(自由变量)并没有释放
#func里保存的是原foo函数对象
2.被装饰的函数有参数
# 要将被装饰的函数参数同等的传递给装饰器的内部函数;
def a(fn):
def b(n):
return fn(n)
return b
@a
def y(n):
return n
y(32)
原因很简单,就是调用y的时候就是在调用内部翻书b.既然y有参数,y在b内部被处理
故肯定要给b传同样的参数
- 被装饰的函数有不定长的参数
from time import ctime, sleep
def timefun(fn):
def w(*args, **kwargs):
print(f'{fn.__name__} called at {ctime()}')
fn(*args, **kwargs)
return w
@timefun
def foo(a,b,c):
print(a+b+c)
foo(1,1,2)
sleep(3)
foo(3,5,8)
foo called at Wed Jun 19 15:07:00 2019
4
foo called at Wed Jun 19 15:07:03 2019
16
from time import ctime, sleep
def timefun(func):
def wrappedfunc():
print("%s called at %s"%(func.__name__, ctime()))
func()
return wrappedfunc
@timefun
def foo():
print("I am foo")
@timefun
def getInfo():
return '----hahah---'
foo()
sleep(2)
foo()
print(getInfo())
# 看不懂为什么会打印出none的往后看
foo called at Wed Jun 19 15:08:17 2019
I am foo
foo called at Wed Jun 19 15:08:19 2019
I am foo
getInfo called at Wed Jun 19 15:08:19 2019
None
def haha():
return 'hahahaah'
print(haha())
hahahaah
def ha(fn):
print('fasfsafsadf')
fn()
print(ha(haha))
fasfsafsadf
None
def h():
'h'
print(h())
# 为什么会出现none可以理解
None
# 如果修稿为return func()
from time import ctime, sleep
def timefun(func):
def wrappedfunc():
print("%s called at %s"%(func.__name__, ctime()))
return func()
return wrappedfunc
@timefun
def foo():
print("I am foo")
@timefun
def getInfo():
return '----hahah---'
foo()
sleep(2)
foo()
print(getInfo())
foo called at Wed Jun 19 15:16:00 2019
I am foo
foo called at Wed Jun 19 15:16:02 2019
I am foo
getInfo called at Wed Jun 19 15:16:02 2019
----hahah---
总结:一般情况下为了让装饰器更通用,可以有return
5.装饰器带参数,在原有装饰器的基础上,设置外部变量
from time import ctime, sleep
def timefun_arg(pre="hello"):
def timefun(func):
def wrappedfunc():
print("%s called at %s %s"%(func.__name__, ctime(), pre))
func() # 与return func() 作用相同,因为foo的函数里是print
return wrappedfunc #
return timefun #
@timefun_arg()
def foo():
print("I am foo")
@timefun_arg("python")
def too():
print("I am too")
foo()
sleep(2)
foo()
too()
sleep(2)
too()
foo called at Wed Jun 19 15:23:30 2019 hello
I am foo
foo called at Wed Jun 19 15:23:32 2019 hello
I am foo
too called at Wed Jun 19 15:23:32 2019 python
I am too
too called at Wed Jun 19 15:23:34 2019 python
I am too
# 可以理解为
foo()==timefun_arg()(foo)()
foo called at Wed Jun 19 15:21:49 2019 hello
I am foo
wrappedfunc called at Wed Jun 19 15:21:49 2019 hello
foo called at Wed Jun 19 15:21:49 2019 hello
I am foo
True
6.类装饰器,比较少用
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()
---初始化---
func name is test
---装饰器中的功能---
----test---