Python学习笔记:Chaper3高级特性

一级目录

二级目录

三级目录

3.1高级特性

3.1.1切片

List在python语言中很常用,取出list中的元素也是频繁的操作。如何取出list中元素,学习过循环后,自然地想到了使用循环结构:

#如何取一个List的前三个元素
grade=[43,24,66,3,7,79]
#直接想法使用for循环,利用下标取出
#注意range(x)得到的是0到小于x的一个序列,序列是不包括x的
r=[]
for i in range(3):
    r.append(grade[i])
print(r)

但是频繁取出元素就需要频繁地写循环结构,为了化简这种操作,python提供了切片(Slice)语法:

#若要频繁地从List中取元素,写循环会变得比较繁琐,
# python语言提供了切片Slice的语法:
x=grade[0:3]
print(x)

其中grade[0:3]表示从索引0开始取,直到索引3为止,但不包括索引3。即索引012,正好是3个元素。如果索引是0的话,那么可以省略,即上面的语句可以直接写成:grade[:3]

当然也可以从1开始,取到3:grade[1:3],,一共取出两个元素

类似的,既然Python支持L[-1]取倒数第一个元素,那么它同样支持倒数切片:

y=grade[-2:-1]
print(y)
y=grade[-2:]
print(y)

上述两次输出结果是不一样的:

[7]
[7, 79]

切记最后一个元素的索引为-1

下面经过实例展示切片的方便之处:

#举例说明切片的用法:

#先产生一个0-100的序列:
myList=list(range(101))
print(myList)
#取出前十个数:
print(myList[:10])
#取出倒数十个数:
print(myList[-10:])
#取出前11-20个数
print(myList[10:20])
#前10个数,每两个一取
print(myList[:10:2])#这里和matlab的语法区分一下
#所有的数:
print(myList[:])
#所有的数,每5个一取
print(myList[::5])

tuple也是一种list,唯一区别是tuple不可变。因此,tuple也可以用切片操作,只是操作的结果仍是tuple:

>>> (0, 1, 2, 3, 4, 5)[:3]
(0, 1, 2)

字符串'xxx'也可以看成是一种list,每个元素就是一个字符。因此,字符串也可以用切片操作,只是操作结果仍是字符串:

>>> 'ABCDEFG'[:3]
'ABC'
>>> 'ABCDEFG'[::2]
'ACEG'

很多语言,比如java,c语言等对于字符串都提供了求子串subString()类似的函数或者方法,但是python没有,在python中只要使用切片就能简单完成这项操作。

#tuple也可以切片,只不过切片的结果还是tuple
myTuple=(0,1,44,2,4)
print(myTuple[1:3])

#字符串也可以切片
string="pythonyyds"
print(string[-4:])#yyds

3.1.2迭代

迭代:通过for循环来遍历一个list或者一个tuple

在python中迭代是通过for...in来进行的。(其他的语言,比如C语言是通过下标来实现的)python的for不仅可以作用在list或者tuple这种有下标的类型上,可以作用在一切可迭代对象上,比如dict或者set,没有下标但是是可迭代的。

myDict={"Mike":43,"Judy":22,"Thomas":23}
#默认情况下for in迭代的是dict中的key
for key in myDict:
    print(key)
#迭代value
for value in myDict.values():
    print(value)
#key-value都迭代:
for k,v in myDict.items():
    print(k,v)

当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。

那么,如何判断一个对象是可迭代对象呢?方法是通过collections模块的Iterable类型判断:

语句如下:

# 如何判断一个对象是可迭代的?
# 使用 isinstance(x,iterable)语句,
# 返回True或者False
from collections.abc import Iterable
print(isinstance("ABC",Iterable))
print(isinstance(123,Iterable))

最后一个小问题,如果要对list实现类似Java那样的下标循环怎么办?Python内置的enumerate函数可以把一个list变成索引-元素对,这样就可以在for循环中同时迭代索引和元素本身:

#使用enumerate函数使list变成索引-元素对
#python中可以在一个循环中同时引用两个变量
for x,y in enumerate(["A","B","C"]):
    print(x,y)

3.1.3列表生成器

列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式

直接看下面的代码,使用list()生成一个列表

#使用列表生成器生成一个0-10的列表
print(list(range(11)))
#生成[1×1,2×2,3×3...]
#方法一:循环
L=[]
for i in range(11):
    L.append(i**2)
print(L)

#方法二:直接使用一行语句替代
print([x*x for x in range(11)])
#注意把x*x这个结果写在前面,后面跟上正常的for循环语句即可
print([x**3 for x in range(11)])

for循环后面还可以加上if判断,这样我们就可以筛选出仅偶数的平方

# 列表生成器后面还能加上if条件
print([x**2 for x in range(11) if x%2==0])

#使用两层循环生成全排列
print([x+y for x in "ABCD" for y in "EFGH"])

#注意,三层以及三层以上的for循环语句就很少会使用了

还能够加上函数调用等操作:

myDict={"Mike":44,"Judy":23,"Potter":6}
#前面讲述过使用for循环来进行key和value的迭代:
#同时迭代:
for x,y in myDict.items():
    print("(",x,y,")")
#只迭代value:
for x in myDict.values():
    print(x)
myDict2={'xyz': 'A', 'yxz': 'B', 'zxy': 'C'}
#现在使用列表生成器来完成这项功能:
print([k+'='+v for k,v in myDict2.items()])

#将一个list中的首字母全部大写
print([k.title() for k in myDict2])

特别的,列表生成器的if...else语句该如何写:记住if…else要放在for之前即可,即:

#if...else结构:
print([x if x%2==0 else -x for x in range(11)])

3.1.4生成器

通过列表生成式,我们可以直接创建一个列表。

但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素空间都浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。

在Python中,这种一边循环一边计算的机制,称为生成器:generator

对比列表生成器,生成器只是将列表生成器的[ ]表示的列表换成了( )。有下面的代码:

#产生0-10的列表
l=[x for x in range(11)]
print(l)

#产生0-10的generator
g=(x for x in range(11))
print(g)
#上一行代码的结果是:<generator object <genexpr> at 0x0000014042360740>
#generator保存的是算法,而不是结果

最难理解的就是generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

3.1.5迭代器

总结一下之前的知识,可以用于for循环的数据类型有以下几种:

  • 集合数据类型

    • list
    • tuple
    • dict
    • set
    • str
  • generator

这些可以用for循环迭代的对象统称为可迭代对象

要判断一个对象是否为可迭代对象可以使用isinstance(x,Iterable)语句。具体见3.1.2迭代。

其中generator是Iterator对象,但是list,tuple,dict不是Iterator

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

那么如何判断可迭代,和是否是Iterator呢?

  • 可迭代:可以用for去遍历

  • Iterator:可以用next()函数去获取下一个

  • 集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

    Python的for循环本质上就是通过不断调用next()函数实现的。

3.2函数式编程

3.2.1高阶函数

3.2.1.1map和reduce
1.map

功能:map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用于序列的每个元素,并把结果作为新的Iterator返回。

举例说明,将函数 f ( x ) = x 2 f(x)=x^2 f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,使用map()实现如下:

#讲述map函数
L=[1,2,3,4,5]
#不使用map函数的情况下,将list中的所有的元素变为他的平方
#法1:for循环迭代:
R1=[]
for i in L:
    R1.append(i**2)
print("for循环结果:",R1)
#法2:列表生成器:
R2=[x*x for x in L]
print("列表生成器结果:",R2)

#使用map函数
def f(x):
    return x**2
R3=map(f,L)
print("map函数结果:",R3)

运行结果如下:

for循环结果: [1, 4, 9, 16, 25]
列表生成器结果: [1, 4, 9, 16, 25]
map函数结果: <map object at 0x000001C768173160>

可以看出for循环和列表生成器的结果都是一个列表,但是map不同,返回的是一个Iterator对象。

#要想将结果进一步转化成List而不是一个Iterator可以使用list函数:
R4=list(map(f,L))
print("map函数转化:",R4)

列表生成器可以和map函数完成相似的功能,甚至更多

2.reduce

reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

下面给出几个例子,不做具体说明

①序列求和的reduce实现:

特别注意,使用reduce函数要加上一句from functools import reduce

from functools import reduce    #python3要求

#reduce函数的作用是对上一次产生的结果继续和序列的下一个元素做运算。
#例1:使用reduce实现累加运算:
def toSum(x,y):
    return x+y
L=[1,2,3,4,5]
print("使用reduce函数求和结果:",reduce(toSum,L))
#其实直接使用sum函数也可以解决:
print("使用sum函数求和结果:",sum(L))

②把序列[1, 3, 5, 7, 9]变换成整数13579

from functools import reduce
#reduce实例二:
# 把序列[1, 3, 5, 7, 9]变换成整数13579
L=[1,3,5,7,9]
def toInt(x,y):
    return x*10+y
print(reduce(toInt,L))

③考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数:

from functools import reduce
#使用reduce将str转换为int
string="983537"
def charToNum(s):
    digits={'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]
def fn(x,y):
    return x*10+y
print(reduce(fn,map(charToNum,string)))

可以看出reduce函数可以作用于一个Iterator

3.2.1.2 filter

filtermap相似,也接收一个函数和一个序列

map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。(有点像列表生成器里面的if…else语句的作用了)

最后要注意filter返回的也是一个Iterator

给出下面的例子,在一个序列中保留奇数,删除偶数

#使用filter保留一个序列中的奇数,删除偶数
L=[1,2,3,4,5,6]
def is_odd(n):
    if n%2==0:
        return True
    return False
print(list(filter(is_odd,L)))

把一个序列中的空字符串删掉,可以这么写:

#把字符串中的空字符删掉
def not_empty(s):
    return s and s.strip()
L="A B   D DDD"
print(list(filter(not_empty,L)))
  • 使用filter求素数

3.2.1.3 sorted函数

排序的核心是比较两个数的大小,而对于dict或者字符串来说,直观上是无法比较他们的大小的,因此比较的过程必须使用函数进行抽象。

Python内置的sorted函数可以对list进行排序:

#使用sorted对列表进行排列
L=[1,4,6,22,5,3,55,9,10,-1,32]
print(sorted(L))

特别的是sort()可以接受一个key来实现自定义的排序。

原理是key所规定的函数将作用在list的每一个元素上,这样有了一个已经处理过的listsorted函数再对这个已经处理过的list进行排序

举例如下:

①按照绝对值大小进行排序

#sorted可以接收一个key,用来实现自定义排序
L=[1,4,6,22,5,3,55,9,10,-1,32,-97]
#实现按照绝对值大小进行排序:
print(sorted(L,key=abs))
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]

key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。对比原始的list和经过key=abs处理过的list:

list = [36, 5, -12, 9, -21]

keys = [36, 5,  12, 9,  21]

然后sorted()函数按照keys进行排序,并按照对应关系返回list相应的元素:

keys排序结果 => [5, 9,  12,  21, 36]
                |  |    |    |   |
最终结果     => [5, 9, -12, -21, 36]

②对于字符串的忽略大小写排序

忽略大小写排序可以将接收的key设置为函数将原有的列表变为全大写或者全小写,再利用sorted函数进行排序即可。

#实现对字符串的忽略大小写排序:
L=['bob', 'about', 'Zoo', 'Credit']
print(sorted(L,key=str.lower))

要进行反向排序,不必改动key函数,可以传入第三个参数reverse=True

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

从上述例子可以看出,高阶函数的抽象能力是非常强大的,而且,核心代码可以保持得非常简洁。

3.3模块

python中本身就内置了很多有用的模块,只要安装完毕就可以使用。

以内置的sys模块为例,编写一个hello的模块。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

' a test module '

__author__ = 'Michael Liao'

import sys

def test():
    args = sys.argv
    if len(args)==1:
        print('Hello, world!')
    elif len(args)==2:
        print('Hello, %s!' % args[1])
    else:
        print('Too many arguments!')

if __name__=='__main__':
    test()

第1行和第2行是标准注释,第1行注释可以让这个hello.py文件直接在Unix/Linux/Mac上运行,第2行注释表示.py文件本身使用标准UTF-8编码;

第4行是一个字符串,表示模块的文档注释,任何模块代码的第一个字符串都被视为模块的文档注释;

第6行使用__author__变量把作者写进去,这样当你公开源代码后别人就可以瞻仰你的大名;

以上就是Python模块的标准文件模板,当然也可以全部删掉不写,但是,按标准办事肯定没错。

后面开始就是真正的代码部分。

你可能注意到了,使用sys模块的第一步,就是导入该模块:

import sys

导入sys模块后,我们就有了变量sys指向该模块,利用sys这个变量,就可以访问sys模块的所有功能。

sys模块有一个argv变量,用list存储了命令行的所有参数。argv至少有一个元素,因为第一个参数永远是该.py文件的名称,例如:

运行python3 hello.py获得的sys.argv就是['hello.py']

运行python3 hello.py Michael获得的sys.argv就是['hello.py', 'Michael']

最后,注意到这两行代码:

if __name__=='__main__':
    test()

当我们在命令行运行hello模块文件时,Python解释器把一个特殊变量__name__置为__main__,而如果在其他地方导入该hello模块时,if判断将失败,因此,这种if测试可以让一个模块通过命令行运行时执行一些额外的代码,最常见的就是运行测试。

我们可以用命令行运行hello.py看看效果:

$ python3 hello.py
Hello, world!
$ python hello.py Michael
Hello, Michael!

如果启动Python交互环境,再导入hello模块:

$ python3
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 23 2015, 02:52:03) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import hello
>>>

导入时,没有打印Hello, word!,因为没有执行test()函数。

调用hello.test()时,才能打印出Hello, word!

>>> hello.test()
Hello, world!

作用域

在一个模块中,我们可能会定义很多函数和变量,但有的函数和变量我们希望给别人使用,有的函数和变量我们希望仅仅在模块内部使用。在Python中,是通过_前缀来实现的。

正常的函数和变量名是公开的(public),可以被直接引用,比如:abcx123PI等;

类似__xxx__这样的变量是特殊变量,可以被直接引用,但是有特殊用途,比如上面的__author____name__就是特殊变量,hello模块定义的文档注释也可以用特殊变量__doc__访问,我们自己的变量一般不要用这种变量名;

类似_xxx__xxx这样的函数或变量就是非公开的(private),不应该被直接引用,比如_abc__abc等;

之所以我们说,private函数和变量“不应该”被直接引用,而不是“不能”被直接引用,是因为Python并没有一种方法可以完全限制访问private函数或变量,但是,从编程习惯上不应该引用private函数或变量。

private函数或变量不应该被别人引用,那它们有什么用呢?请看例子:

def _private_1(name):
    return 'Hello, %s' % name

def _private_2(name):
    return 'Hi, %s' % name

def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)

我们在模块里公开greeting()函数,而把内部逻辑用private函数隐藏起来了,这样,调用greeting()函数不用关心内部的private函数细节,这也是一种非常有用的代码封装和抽象的方法,即:

外部不需要引用的函数全部定义成private,只有外部需要引用的函数才定义为public。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Blanche117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值