在 Python 中, 整数、 字符串、字典、函数、类都是一等对象(均可作为常规变量处理)
python函数定义
在 Python 中, 函数是一等对象。
“一等对象”定义为满⾜下述条件的程序实体:
1.在运行时创建
2.能赋值给变量或数据结构中的元素
3.能作为参数传给函数
4.能作为函数的返回结果
由于以上4点特性,python具有函数式编程的风格
变量
变量解析
局部作用域-》外部嵌套作用域-》全局作用域-》内建作用域
a=1 def f(): print a f() ######### 1
def f(): x=1 def fun(): print x return fun f()() ######### 1
def f(): m = 0 def f2(): m = 1 print m print m f2() print m f() ######### 0 1 0
变量修改
1.python默认按引用赋值,即使是数字等基本类型也是按引用赋值
2.函数内部默认不能修改函数外部变量的引用,使用global可以改变全局变量的引用,但无法改变函数外部作用域(即外层嵌套函数)的引用
3.函数内部可以修改外部容器变量的值
# python默认按引用赋值 a = 1 b = a x = [1, 2] y = x print('id(a):', id(a), 'id(b):', id(b)) print('id(x):', id(x), 'id(y):', id(y)) print('\n') b = 2 y = [3, 4] print('id(a):', id(a), 'id(b):', id(b)) print('id(x):', id(x), 'id(y):', id(y)) ### id(a): 1445593552 id(b): 1445593552 id(x): 1913825104328 id(y): 1913825104328 id(a): 1445593552 id(b): 1445593584 id(x): 1913825104328 id(y): 1913825104264
# 修改引用 x = [1] def f(): print id(x),x def fun(): global x x=[2] print id(x),x return fun f()() ####### 60219080 [1] 60304264 [2]
# 修改引用 # global代表修改全局变量,不能修改外部做作用域的局部变量(如外层嵌套函数的局部变量) x=100 def fun(): global x x += 1 print id(x), x print id(x),x fun() ####### 44329760 100 44329736 101
# 修改值而非引用 def f(): x = [1] print id(x),x def fun(): x[0]=2 print id(x), x return fun f()() ####### 54189768 [1] 54189768 [2]
# 修改值而非引用 x = [1] def f(): def fun(): print id(x), x x[0]=2 print id(x), x return fun f()() ######## 52485832 [1] 52485832 [2]
函数参数
默认参数
默认参数有个坑,如果是可变对象,每次调用函数会保存上次的数据,因为默认参数指向一个全局的对象。
所以,定义默认参数要牢记一点:默认参数指向可变对象要注意可能出现的问题!至于什么是不可变对象,click this
示例一:这个很清楚,b是全局变量,每次调用函数时a都是指向b,即每次调用函数时a就是b
b = []
def fun1(a):
a.append(1)
print(a)
fun1(b) # [1]
fun1(b) # [1,1]
fun1(b) # [1,1,1]
示例二:这个也很清楚,示例一的改版,b是全局变量,每次调用函数时a都是指向b,即每次调用函数时a就是b
b = []
def fun1(a=b):
a.append(1)
print(a)
fun1() # [1]
fun1() # [1,1]
fun1() # [1,1,1]
示例三: 重点来了,a=[ ]中的[]其实就是全局对象,只不过这个对象没有全局变量b指向它
def fun1(a=[]):
a.append(1)
print(a)
fun1() # [1]
fun1() # [1,1]
fun1() # [1,1,1]
print(id([1])) # 1868746959944
print(id([1,1])) # 1868746959944
print(id([1,1,1])) # 1868746959944
多类型传值
(*解析非字典数据,**解析字典)
n [1]: def fun(x,y,z): ...: return x+y+z ...: In [2]: t=(1,2,3) In [3]: t2=(4,5) In [4]: fun(*t) Out[4]: 6 In [2]: l=[1,2,3] In [3]: fun(*l) Out[3]: 6 In [5]: fun(3,*t2) Out[5]: 12 In [6]: d={'x':1,'y':3,'z':5} In [7]: d2={'y':3,'z':5} In [8]: fun(**d) Out[8]: 9 In [9]: fun(1,**d2) Out[9]: 9
冗余参数
(*把参数封装成元组,**把参数封装成字典)
In [1]: def fun(x,*args,**kwargs): ...: print x ...: print args ...: print kwargs ...: In [2]: fun(1) () {} In [3]: fun(1,2) (2,) {} In [4]: fun(1,2,'a') (2, 'a') {} In [5]: fun(1,2,'a',[1,2,],y=2) (2, 'a', [1, 2]) {'y': 2} In [6]: fun(1,2,'a',[1,2,],y=2,z=3) (2, 'a', [1, 2]) {'y': 2, 'z': 3} In [7]: fun(1,2,'a',[1,2,],*(1,2)) (2, 'a', [1, 2], 1, 2) {} In [8]: fun(1,2,'a',[1,2,],a=3,**{'b':666}) (2, 'a', [1, 2]) {'a': 3, 'b': 666}
lambda表达式
定义:精简的函数
语法:lambda 参数:表达式
优点:1.省去定义函数的过程让代码更简洁;2.不会被重复使用的函数用lamba替代可省略函数的命名;3.有时让代码更容易理解
In [1]: def add(x,y): return x+y ....: In [2]: reduce(add,range(1,101)) Out[3]: 5050 In [4]: reduce(lambda a,b:a+b,range(1,101)) Out[5]: 5050 In [5]: reduce(lambda a,b:a*b,range(1,6)) Out[6]: 120
内建函数
查看内置函数:
官方文档: https://docs.python.org/2.7/library/index.html
常用内建函数
数学 abs max min len divmod pow round
callable type isinstance cmp range xrange
类型转换 int long float complex
字符串处理函数:str.capitalize str.replace str.splite str.join string模块(和字符串内建函数功能类似但用法稍微更复杂)
序列处理函数: filter zip map reduce
In [9]: def f(x): if x%2 == 0: return True ...: In [10]: filter(f,range(10)) //或者filter(lambda x:x%2==0,range(10)) Out[10]: [0, 2, 4, 6, 8] In [11]: l1=[1,2,3] In [13]: l2=['a','b','c'] In [14]: zip(l1,l2) Out[14]: [(1, 'a'), (2, 'b'), (3, 'c')] In [17]: def f(x): return x**2 ....: In [18]: map(f,l1) //或者map(lambda x: x**2, [1,2,3]) Out[18]: [1, 4, 9] In [20]: l2=[4,5,6] In [24]: def f(x,y): return x*y ....: In [25]: map(f,l1,l2) //或者map(lambda x,y: x*y, [1,2,3],[4,5,6]) Out[25]: [4, 10, 18]
推导式
推导式是C、C++、Java里面没有的语法,但是,是Python里面使用非常广泛,是特别推荐的用法,是for语句的一种简写形式。
列表推导
# [i*2+10 for i in range(10) if i%3==0] #等效于 l = [] for i in range(10): if i % 3 == 0: l.append(i * 2 + 10)
In[1]: [i * 2 + 10 for i in range(10) if i % 3 == 0]
Out[1]: [10, 16, 22, 28]
In [2]: [i*2 if i%3==0 else i for i in range(10) ]
Out[2]: [0, 1, 2, 6, 4, 5, 12, 7, 8, 18]
In[3]: [i for i in range(5) if i % 2 == 0]
Out[3]: [0, 2, 4]
In[4]: [i for i in range(5) if i % 2 == 0 and not i == 2]
Out[4]: [0, 4]
a=[1,2]
b=[a for i in range(4)]
print(b)
[[1, 2], [1, 2], [1, 2], [1, 2]]
集合推导
In [1]: s={i*2+10 for i in range(10) if i%3==0}
In [2]: type(s)
Out[2]: set
In [3]: s
Out[3]: {10, 16, 22, 28}
字典推导
In [4]: dict={i:i*2+10 for i in range(10) if i%3==0}
In [5]: type(dict)
Out[5]: dict
In [6]: dict
Out[6]: {0: 10, 3: 16, 6: 22, 9: 28}
生成器列表推导式
作用:列表生成器如果数据过大,会造成内存压力,此时可用生成器推导式来处理
In [45]: a=(i*2+10 for i in range(10) if i%3==0)
In [46]: a
Out[46]: <generator object <genexpr> at 0x000000000445CC60>
In [47]: type(a)
Out[47]: generator
In [48]: a.next()
Out[48]: 10
In [49]: a.next()
Out[49]: 16
In [50]: a.next()
Out[50]: 22
In [51]: a.next()
Out[51]: 28
for本质
优点(和其它循环(whilei)相比的):比whlle等循环功能强大,不仅遍历的对象种类多,而且比普通循环效率更高(自动把遍历对象生成迭代器)
定义:遍历可迭代对象(string,list,dict等),如果是遍历可迭代对象,for会自动把in后面的可迭代对象转换成迭代器,把所有元素依次加载到内存,遍历完成后自动处理异常
for i in t: # t被for转换成t.__iter__()
print(i)
等效于
t = [1, 2, 3, 4, 5].__iter__() # 可以通过__iter__()方法或iter()内建函数把Iterable类型变成Iterator对象。
# 循环:
while True:
try:
# 获得下一个值:
i = t.next()
print(i)
except StopIteration:
# 遇到StopIteration就退出循环
break
函数的解析与调用
函数解析时,执行def 这行,调用时再继续往下执行
res = [lambda: x for x in range(1, 3)]
print(res[0]())
###
2
等效于
list = []
for x in range(1,3):
def func():
return x
list.append(func)
print(list[0]())
###
2
修正bug
res = [lambda n=x: n for x in range(1, 3)]
print(res[0]())
list = []
for x in range(1,3):
def func(n=x):
return n
list.append(func)
print(list[0]())
###
1
1
最终版
res = [lambda x=x: x for x in range(1, 3)]
print(res[0]())
list = []
for x in range(1,3):
def func(x=x):
return x
list.append(func)
print(list[0]())
###
1
1
迭代器
为什么要用迭代器:
优点
1:迭代器提供了一种不依赖于索引的取值方式,这样就可以遍历那些没有索引的可迭代对象了(字典,集合,文件)
2:迭代器与列表比较,迭代器是惰性计算的,更节省内存(同一时刻只有一个值在内存中)
缺点:
1:无法获取迭代器的长度,使用不如列表索引取值灵活
2:一次性的,只能往后取值,不能倒着取值
#可迭代对象:只要对象本身有__iter__方法,那它就是可迭代对象
# d={'a':1,'b':2,'c':3}
# d.__iter__ #iter(d)
#
#
#执行对象下的__iter__方法,得到的结果就是迭代器
# i=d.__iter__()
#
# print(i.__next__())
# print(i.__next__())
# print(i.__next__())
# print(i.__next__())
from collections import Iterable,Iterator s='hello' l=[1,2,3] t=(1,2,3) d={'a':1} set1={1,2,3,4} f=open('a.txt') #都是可迭代的 # s.__iter__() # l.__iter__() # t.__iter__() # d.__iter__() # set1.__iter__() # f.__iter__() # print(isinstance(s,Iterable)) # print(isinstance(l,Iterable)) # print(isinstance(t,Iterable)) # print(isinstance(d,Iterable)) # print(isinstance(set1,Iterable)) # print(isinstance(f,Iterable)) #查看是否是迭代器 print(isinstance(s,Iterator)) print(isinstance(l,Iterator)) print(isinstance(t,Iterator)) print(isinstance(d,Iterator)) print(isinstance(set1,Iterator)) print(isinstance(f,Iterator))
生成器
作用:
1.逐步生成序列,不用像列表一样初始化时就要开辟所有的空间(相当于2.x的xrange)
2.模拟并发:协程(Python实现协程最简单的方法,就是使用yield)
定义:如果函数中包含yield语法,那这个函数就会变成生成器,这个函数调用时返回一个迭代器,生成器属于迭代器
生成器与return有何区别?
return只能返回一次函数就彻底结束了,而yield能返回多次值;生成器只能遍历一次
yield到底干了什么事情:
1.yield把函数变成生成器-->迭代器
用return返回值能返回一次,而yield返回多次
函数在暂停以及继续下一次运行时的状态是由yield保存
总结yield的功能:
1.相当于把__iter__和__next__方法封装到函数内部
2.与return比,return只能返回一次,而yield能返回多次
3.函数暂停已经继续运行的状态是通过yield保存的
from collections import Iterator #生成器就是一个函数,这个函数内包含有yield这个关键字 def test(): print('one') yield 1 #return 1 g=test() print(g) print(isinstance(g,Iterator)) ### <generator object test at 0x000001E80F8F0780> True
from collections import Iterator #生成器就是一个函数,这个函数内包含有yield这个关键字 def test(): print('one') yield 1 #return 1 g=test() print(g) print(isinstance(g,Iterator)) print(next(g)) ### <generator object test at 0x0000023F1F5E0780> True one #调用next方法时,生成器才执行 1
def countdown(n): print('start coutdown') while n > 0: yield n #1 n-=1 print('done') g=countdown(5) # print(g) # print(next(g)) # print(next(g)) # print(next(g)) # print(next(g)) # print(next(g)) # print(next(g)) # for i in g: #iter(g) # print(i) # while True: # try: # print(next(g)) # except StopIteration: # break
>>> def createGenerator() : ... mylist = range(3) ... for i in mylist : ... yield i*i ... >>> mygenerator = createGenerator() # create a generator >>> print(mygenerator) # mygenerator is an object! <generator object createGenerator at 0xb7555c34> >>> for i in mygenerator: ... print(i) 0 1 4
协程函数
当一个函数在执行过程中被阻塞时,就用yield挂起,然后执行另一个函数。当阻塞结束后,可以用next()或者send()唤醒。相比多线程,协程的好处是它在一个线程内执行,避免线程之间切换带来的额外开销,而且多线程中使用共享资源,往往需要加锁,而协程不需要,因为代码的执行顺序是你完全可以预见的,不存在多个线程同时写某个共享变量而导致出错的情况。
#如果在一个函数内部yield的使用方式是表达式形式的话,如x=yield,那么该函数成为协程函数 def eater(name): print('%s start to eat food' %name) food_list=[] while True: food=yield food_list print('%s get %s ,to start eat' %(name,food)) food_list.append(food) print('done') e=eater('钢蛋') # print(e) print(next(e)) print(e.send('包子')) print(e.send('韭菜馅包子')) print(e.send('大蒜包子'))
yield的表达式形式: food=yield def eater(name): print('%s start to eat' %name) while True: food=yield print('%s eat %s' %(name,food)) e=eater('zhejiangF4') #e.send与next(e)的区别 #1.如果函数内yield是表达式形式,那么必须先next(e) #2.二者的共同之处是都可以让函数在上次暂停的位置继续运行,不一样的地方在于 send在触发下一次代码的执行时,会顺便给yield传一个值
def consumer(): last = '' while True: receival = yield last if receival is not None: print 'Consume %s' % receival last = receival def producer(gen, n): gen.next() x = 0 while x < n: x += 1 print 'Produce %s' % x last = gen.send(x) gen.close() gen = consumer() producer(gen, 5)
闭包
说明:闭包指的是内层函数,之所以叫闭包,闭是因为在外层函数内,包是因为和外层函数的变量绑定在一起。
定义:
1.外层函数返回内层函数,
2.外层函数的变量被内层函数引用(这个被引用的自由变量将和这个内层函数一同存在)
x = 1
def f1():
x = 2
y = 1
def f2():
print(x)
y
return f2
f = f1(x)
f()
print(f.__closure__[0].cell_contents)
print(f.__closure__[1].cell_contents)
print(f.__closure__)
###
2
2
1
(<cell at 0x0000015A36F16C78: int object at 0x0000000051A901F0>, <cell at 0x0000015A36F16CA8: int object at 0x0000000051A901D0>)
def f1(x):
# x = 2
y = 1
def f2():
print(x)
y
return f2
f = f1(x)
f()
print(f.__closure__[0].cell_contents)
print(f.__closure__[1].cell_contents)
print(f.__closure__)
###
1
1
1
(<cell at 0x00000188B2B36C78: int object at 0x0000000051A901D0>, <cell at 0x00000188B2B36CA8: int object at 0x0000000051A901D0>)
x = 1
def f1(x):
x = 2
y = 1
def f2():
print('ok')
return f2
f = f1(x)
f()
print(f.__closure__)
###
ok
None
作用:和类一样抽象对象,闭包抽象了函数,目的都是减少函数代码,提高代码复用性。应用很多,举例如下
1.嵌套函数(用于先准备一个函数,即外层函数执行完后,内层函数仍可以使用外层函数的变量),装饰器就是这种应用
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)
#######
6
25
这个例子中,函数line与环境变量a,b构 成闭包。在创建闭包的时候,我们通过line_conf的参数a,b说明了这两个环境变量的取值,这样,我们就确定了函数的最终形式(y = x + 1和y = 4x + 5)。我们只需要变换参数a,b,就可以获得不同的直线表达函数。由此,我们可以看到,闭包也具有提高代码可复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。利用闭包,我们实际上创建了泛函。line函数定义一种广泛意义的函数。这个函数的一些方面已经确定(必须是直线),但另一些方面(比如a和b参数待定)。随后,我们根据line_conf传递来的参数,通过闭包的形式,将最终函数确定下来。
2.保存上次的运行环境(外层函数的的生命周期结束之后,外层函数的变量不被销毁)
def outer(name):
count=[0]
def inner():
count[0]+=1
print 'Hello,', name,', ', str(count[0])+' access!'
print count
return inner
hello = outer('hy')
hello()
hello()
hello()
########
Hello, hy , 1 access!
[1]
Hello, hy , 2 access!
[2]
Hello, hy , 3 access!
[3]
这里面调用outer的时候就产生了一个闭包——inner,并且该闭包持有自由变量——count,因此这也意味着,当函数outer的生命周期结束之后,count这个变量依然存在,因为它被闭包引用了,所以不会被回收。
另外再说一点,闭包并不是Python中特有的概念,所有把函数做为一等公民的语言均有闭包的概念。不过像Java这样以class为一等公民的语言中也可以使用闭包,只是它得用类或接口来实现。
装饰器
函数装饰器
装饰函数
定义:装饰器是特殊的闭包,特殊在于装饰器的参数除了可以是基本数据类型外,还可以是函数或类
作用:不改动原函数、不改变原函数调用方式的前提下,扩展函数功能,遵循了开放封闭原则
需求一:为源码函数扩展功能,且不改变调用方式
# _*_ encoding:utf-8 _*_ def login(func): print("passer user vertification...") func() def tv(): print("Welcome [%s] to home page") tv = login(tv) # tv的值为None tv() # 这步出错:TypeError: 'NoneType' object is not callable # 输出 passer user vertification... File "D:/PythonProject/0313/str.py", line 14, in <module> tv() TypeError: 'NoneType' object is not callable 问题: 1.调用方法改变了 2.调用端还没有调用 tv(),扩展的功能就先执行了
def login(func): print("passer user vertification...") return func def tv(): print("Welcome [%s] to tv page") tv = login(tv) #tv的值为tv的内存地址 tv() # 输出 # passer user vertification... # Welcome [%s] to tv page # # 问题: # 调用端还没有调用 tv(),扩展的功能就执行执行
def login(func): print("passer user vertification...") return func @login #@login等效于tv = login(tv) def tv(): print("Welcome [%s] to tv page") tv() # 输出 # passer user vertification... # Welcome [%s] to tv page # # 问题: # 调用端还没有调用 tv(),扩展的功能就执行执行
def login(func): def inner(): print("passed user vertification...") func() return inner def home(): print("Welcome [%s] to home page" % name) # @login # tv=login(tv) def tv(): print("Welcome [%s] to tv page") def movie(): print("Welcome [%s] to home movie" % name) tv=login(tv) tv()
def login(func): def inner(): print("passed user vertification...") func() return inner @login # tv=login(tv) def tv(): print("Welcome [%s] to tv page") tv()
需求二:需求一 + 传递参数 + 返回值
def outer(func): #func=tv def inner(args): #参数其实传到了这里 print("passed user vertification...") func(args) return inner @outer # tv=outer(tv),tv=inner,tv()=inner() def tv(name): print("Welcome [%s] to tv page" % name) tv('hy') ####### passed user vertification... Welcome [hy] to tv page
def outer(func): # func=tv def inner(*args, **kwargs): # 参数其实传到了这里 print("passed user vertification...") func(*args, **kwargs) return inner @outer # tv=outer(tv),tv=inner,tv()=inner() def tv(name): print("Welcome [%s] to tv page" % name) tv('hy') ####### passed user vertification... Welcome [hy] to tv page
def outer(func): # func=tv def inner(args): # 参数其实传到了这里 print("passed user vertification...") return func(args) return inner @outer # tv=outer(tv),tv=inner,tv()=inner() def tv(name): print("Welcome [%s] to tv page" % name) return 6 print tv('hy') ######## passed user vertification... Welcome [hy] to tv page 6
def outer(func): # func=tv def inner(*args, **kwargs): # 参数其实传到了这里 print("passed user vertification...") return func(*args, **kwargs) return inner @outer # tv=outer(tv),tv=inner,tv()=inner() def tv(name): print("Welcome [%s] to tv page" % name) return 6 print tv('hy') ######## passed user vertification... Welcome [hy] to tv page 6
需求三:需求二+含参装饰器
def wrapper(*wrapperargs): def outer(func): # func=tv def inner(*args, **kwargs): # 参数其实传到了这里 print("passed user vertification...") print '[%s] looks like very [%s]' % (''.join(args), ''.join(wrapperargs)) return func(*args, **kwargs) return inner return outer @wrapper('shuai') # tv=outer(tv),tv=inner,tv()=inner() def tv(name): print("Welcome [%s] to tv page" % name) return 6 print tv('hy') ######## passed user vertification... [hy] looks like very [shuai] Welcome [hy] to tv page 6
def Before(): print('before') def After(): print('after') def wrapper(before_func, after_func): def outer(func): # func=tv def inner(*args, **kwargs): # 参数其实传到了这里 print("passed user vertification...") before_func() after_func() return func(*args, **kwargs) return inner return outer @wrapper(Before, After) def tv(name): print("Welcome [%s] to tv page" % name) return 6 print(tv('hy')) ####### passed user vertification... before after Welcome [hy] to tv page 6
需求四:多层装饰器
#!/usr/bin/env python # _*_ coding=utf-8 _*_ def outer(func): def inner(*args,**kwargs): print('log') r=func(*args,**kwargs) print('after') return r return inner def outer2(func): def inner(*args,**kwargs): if LOGIN_INFO['is_login']: r=func() return r else: print('please login') return inner #如果套两层装饰器,就是双层装饰器了,当然也有三层,四层,道理类似 #这里大家可能有疑惑,python在解释有 “@+函数”这种格式的语法时,会自动从里向外解读,再从外向内执行, #也就是最里层的原函数被逐层装饰直到最外层,对应例子里,python先把f2(原函数)发给outer2(里层装饰器),被装饰后的outer2的inner再 #被outer(外层装饰器)装饰,最终返回的是outer的inner函数体。 @outer @outer2 def f2(a,v): print('F2') #当然有人问主函数的调用为啥这样写呢,这个会在模块对于的blog中介绍 if __name__ == '__main__': f2
类装饰器
装饰函数
再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
class Outer(object): def __init__(self, func): self._func = func def __call__(self): self._func() @Outer # tv=Outer(tv) def tv(): print("Welcome to tv page") tv() ### Welcome to tv page
functools.wraps
作用:并不改变原有函数的属性,如doc name
def trace(func): """ 装饰器 """ # @wraps(func) def callf(*args, **kwargs): """ A wrapper function """ print("Calling function:{}".format(func.__name__)) # Calling function:foo res = func(*args, **kwargs) print("Return value:{}".format(res)) # Return value:9 return res return callf @trace def foo(x): """ 返回给定数字的平方 """ return x * x if __name__ == '__main__': print(foo.__doc__) print(foo.__name__) ############ A wrapper function callf
from functools import wraps def trace(func): """ 装饰器 """ @wraps(func) def callf(*args, **kwargs): """ A wrapper function """ print("Calling function:{}".format(func.__name__)) # Calling function:foo res = func(*args, **kwargs) print("Return value:{}".format(res)) # Return value:9 return res return callf @trace def foo(x): """ 返回给定数字的平方 """ return x * x if __name__ == '__main__': print(foo.__doc__) print(foo.__name__) ##### 返回给定数字的平方 foo
魔法函数
定义:以双下划线开头和结尾的属性,如__init__ __len__ __getitem__
作用:python会自动调用这些方法,自定义类就可以增加一些特性
字符串