Python高效上手 -- C/C++程序员参考
前言
对于C/C++程序员,python也是必不可少的语言工具。本文主要是根据廖雪峰的python教程整理而来,他的python教程故事性很好,也能step by step的玩起来,但是框架太差了,学一遍之后,很能让人记住知识脉络,因此本文重构了框架,分为七部分,可以帮助初学者摸清知识roadmap。
正文
第一部分,python程序头,输入输出和注释方式
(1)python程序第一行注释:
#!/usr/bin/env python3 // 标准写法,让系统从环境变量找到python解释器
python xxx.py,直接运行xxx.py文件
python -m xxx,把xxx.py当做模块运行
(2)python使用print函数进行输出,如:
print("hello world") //没有“;”
print('hello world') //也可以用单引号
print('hello world', "cao", "yi") //多字符串用逗号隔开
print("100+123 =", 100 + 123) //混合打印
(3)格式化输出:
%d 整数
%f 浮点数
%s 字符串
%x 十六进制整数
python使用%引用变量与数字(用的最多):
print('%2d-%02d' %(3, 1)) //最后一个%不能少
print("%.2f" %3.141514) //没有逗号!
r = 85
print("0x%x" %r)
(4)python使用\实现转义字符,常用的如\n,\t,\:
print("I'm \\ 'very' ok")
print(r'\\\t\\') //使用r' ',告诉编译器这个string是个raw string
//表示内部的符号不转义
(5)python定义变量和使用input函数输入:
name = input()
print(name)
# 更人性化的如下:
name = input("please input your name:")
print("hello", name)
(6)python 使用 # 注释:
# print a value of 1024 * 768
print(1024*768)
''' //python使用单引号注释多行
print("helloworld")
'''
第二部分,python代码块和基本语法
(1)python的缩进应始终采用4个空格(不是一个tab):
a = 100
if a > 0: //以冒号结束时,代表缩进的语句为代码块
print(a) //python使用缩进而不是{
}表示代码块
else:
print(-a)
(2)python的条件判断:
age = 3
if age >= 18: //python的if语句执行是从上往下判断,
print('your age is', age) //如果在某个判断上为Ture,则执行,
print('adult') //且自动忽略下面的分支
elif age >= 6:
print('your age is', age)
print('teenager')
else:
print('your age is', age)
print('kid')
(3)for…in循环
names = ['wang', 'miao', 'cao', 'yi']
for name in names: //“:”不能省
print(name) //依次把list或tuple的元素迭代出来
sum = 0
for x in range(101): //range()函数可以生成整数序列
sum = sum + x //这段代码是求1~100的和
print(sum)
(4)while循环:
sum = 0
n = 99
while n > 0:
sum = sum + n
n = n - 2
print(sum)
(5)利用if和break可以提前退出循环:
n = 1
while n <= 100:
if n > 10:
break
print(n)
n = n + 1
print('end')
(6)利用if和continue可以跳出当前循环,直接开始下一次循环:
n = 0
while n < 10:
n = n + 1
if n % 2 == 0: //有缩进的地方,必须加“:”
continue
print(n)
(7)借助抽象,我们才能不关心底层的具体计算过程,而直接在更高的层次上思考问题。写计算机程序也是一样,函数就是最基本的一种代码抽象的方式。
提示:这句话道出了数学与科学的本质,也是编程的本质。
扩展:高人与普通人的区别也在于高人能借助抽象在更高的层面上更快更好地思考问题。
(8)函数名其实就是指向一个函数对象的引用,完全可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”:
a = bool
print(a(1))
(9)python自定义函数:
def ycao_abs(x): //def不能少,":"不能少
tmp = 0
if x >= 0:
tmp = x
if x < 0:
tmp = -x
return tmp //返回值用return
//如果没有返回值,可以直接写return,相当于return None
(10)python定义空函数:
def nop():
pass //pass的存在不会让程序报错,且能正常执行
(11)使用内置函数isinstance()为函数添加参数检查:
def ycao_abs(x):
if not isinstance(x, (int)):
raise TypeError('bad operand type')
tmp = 0
if x >= 0:
tmp = x
else:
tmp = -x
return tmp
(12)函数返回两个值:
提示:当函数有多个返回值时,其实返回的是一个 tuple
import math //导入math包,类似c语言中的#include <>
def move(x, y, step, angle = 0):
nx = x + step * math.cos(angle)
ny = y - step * math.sin(angle)
return nx, ny
x, y = move(100, 100, 60)
r = move(100, 100, 60)
(13)使用默认参数:
def power(x, n = 2):
s = 1
while n > 0:
s = s * x
n = n - 1
return s
print(power(3))
print(power(3, 5))
(14)可变参数(个数):
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
print(calc(1, 2, 3))
nums = [1, 2, 3, 4]
print(calc(*nums))
(15)关键字参数,即传入一个dict或dict的某几项:
def person(name, age, **kw):
print('name', name, 'age', age, 'other:', kw)
print(person('miao', 18))
//必选参数必须有
print(person('miao', 18, city = 'suzhou', job = 'buyers'))
//关键字参数可多可少,也可以没有
otherMessage = {
'city':'suzhou', 'job':'buyer'}
print(person('miao', 18, city = otherMessage['city']))
print(person('miao', 18, city = otherMessage['city'], job = otherMessage['job']))
print(person('miao', 18, **otherMessage))
(16)python部分内置函数
// 求绝对值函数abs():
print(abs(-99))
// 求最大值函数max():
print(max(2, 4))
// 整数类型转换函数int():
print(int('123'))
print(int(12.44))
// 字符串类型转换函数str():
print(str(12.3))
// 布尔函数bool():
print(bool(-123))
print(bool(''))
// 十六进制转换函数hex():
print(hex(16)) //输出:0x10
// 将数字字符串转换成数字int():
birth = input('input birth:') //input返回的是字符串
birth = int(birth)
// replace()方法可以替换字符串:
// 提示:字符串'abc'是不可更改的,故在执行了replace()方法后其实是返回了一个新的字符串,而原来的字符串并没有改变
a = 'abc'
b = a.replace('a', 'G')
print(b) //输出:Gbc
print(a) //输出:abc
第三部分,python常用数据结构和简便操作
(1)列表list:
classmates = ['cao', 'miao', 'yi'] //定义列表
print(classmates) //打印列表
len(classmates) //求列表长度
classmates.append('miaomiao') //增加列表元素
classmates.insert(1, 'miaoyi') //在固定位置增加列表元素
classmates.pop() //删除列表尾元素
classmates.pop(1) //删除列表固定位置的元素
l = [] //空列表
print(classmates[1]) //打印特定位置的元素
print(classmates[-2]) //打印倒数第二个元素
a = [1,2,3]
b = [4,5,6]
c = a + b //列表相加
print(c) //输出:[1, 2, 3, 4, 5, 6]
a = ['c', 'b', 'a']
a.sort() //sort()方法可以对list进行排序
(2)元组tuple:
classmates = ('yi', 'miao') //定义元组时必须初始化
print(classmates[0])
//元组定义后不能改变,因此无append,insert,pop功能
//因为不可改变,所以安全
t = ('yi',) //定义只有一个元素的元组必须加逗号,不然会与变量命名冲突
(3)字典dict,使用键-值(key-value)存储,key可以是字符串和数字,且必须是唯一的。
// 定义dict并初始化:
d = {
'wang':10, 'cao':20, 'miao':30, 'yi':40}
// 添加一个元素:
d['bob'] = 45
// 删除一个元素:
d.pop('bob')
// 修改一个元素的value:
d['wang'] = 555
// 使用get()方法获取value:
print(d.get('wang'))
(4)set是一组不可重复的key的集合,其不存储value。
// 创建一个set,需要提供一个list作为输入集合:
s = set([1, 3, 5, 2])
// set里的元素并不是有序的,重复元素会被自动过滤
s = set([1, 9, 2, 5, 3, 5 ,7])
// 通过add(key)方法可以添加元素到set中
s.add(12)
// 通过remove(key)方法可以删除元素
s.remove(2)
// set可以做交集、并集运算
s = set([1, 9, 2, 5, 3, 5 ,7])
s1 = set([1, 2, 4, 5, 9])
print(s & s1)
print(s | s1)
(5)Python提供了切片(Slice)操作,可方便快捷的取出list或tuple的特定元素。
L = list(range(100))
print(L[:10]) //取出前十个
print(L[0:10]) //取出前十个
print(L[-10:]) //取出后十个
print(L[10:20]) //取出第11个到第20个
print(L[0:10:2]) //每隔两个取一个
print(L[::5]) //所有元素,每隔五个取一个
L = (0, 1, 2, 3, 4, 5, 6, 7)
print(L[:3]) //输出:(0, 1, 2)
L = 'ASDFGH' //字符串也可以使用切片
print(L[0:4])
(6)使用for循环遍历一个list或tuple或字符串,遍历过程称为迭代Iteration。能直接被for…in迭代的称为可迭代对象Iterable:
一类是集合数据类型,如list,tuple,dict,set,str
一类是generator
d = {
'a':1, 'b':2, 'c':3}
for x in d:
print(x) //字典dict迭代默认输出是key
for y in d.values():
print(y) //此时可以输出dict的value
for x, y in d.items():
print(x, y) //此时输出key和value
for c in 'ABC':
print(c) //遍历字符串
from collections import Iterable
print(isinstance('abc', Iterable)) //检测一个对象是否可迭代
print(isinstance(123, Iterable))
d = ['a', 'b', 'c']
for i, value in enumerate(d): //内置函数enumerate可以为list添加下标
print(i, value)
L = [(1, 1), (2, 4), (3, 9)]
for x, y in L: //循环里引用双变量
print(x, y)
(7)列表生成式List Comprehensions
L = list(range(1, 11)) //range函数可以用来生产顺序列表,非常方便
L = [x * x for x in range(1, 11)]
//这是标准的列表生成式,x*x是列表元素,后面的for循环是数据来源
L = [x * x for x in range(1, 11) if x % 2 == 0]
//通过添加条件判断,进一步进行限制。
//注意:列表生成式中是没有逗号的
import os
L = [d for d in os.listdir('.')]
//将当前目录下所有的文件名构成一个list
d = {
'x':'A', 'y':'B', 'z':'C'}
L = [k + '=' + v for k, v in d.items()]
//输出:['y=B', 'x=A', 'z=C']
//使用双变量同事迭代字典的key和value
L = ['Hello', 'World', "IBM", 'APPle']
L = [s.lower() for s in L]
//lower函数可以将字符串变成小写
L = ['hello', 'world', 12, 'app', None, True]
lowerList = [s.lower() for s in L if isinstance(s, str)]
//列表的元素类型可以不同
//isinstance(s, str)用来判断元素s是否为字符串str,返回True或False
(7)生成器generator:列表需要一定的存储空间存储数据,而生成器是一边运算一边生成数值
// 第一种创建方式:
g = (x * x for x in range(11)) //将列表生成式的[]换成(),便是生产器,返回的是生成器对象
print(next(g)) //可以使用next函数获取generator的下一个值,但一般不用
print(next(g))
for n in g: //generator也是可迭代的,一般都是用for循环遍历
print(n)
// 第二种创建方式:
def fib(max): //这个是经典的求斐波那契数列的函数
i, a, b = 0, 0, 1
while i < max:
ans = b
a, b = b, a + b
i = i + 1
return ans
def fib(max):
i, a, b = 0, 0, 1
while i < max:
yield b //当函数使用了yield关键字,这个函数就变成了generator
a, b = b, a + b
i = i + 1
n = fib(10)
//使用next函数遍历generator,首先要生成一个generator对象!!!
print(next(n))
print(next(n))
print(next(fib(10))) //直接这么用是不行的
print(next(fib(10)))
for i in fib(10): //使用for循环遍历,这一种和下一种是一样的
print(i)
//遍历时,每次执行到yield时,就会返回一个值,并停止运行,而下次循环会接着上次的位置执行
//当遇到数据比较大时,使用generator比list能有效节约内存
n = fib(10)
for i in n:
print(i)
第四部分,python模块,面向对象以及try–catch
(1)模块就是C语言中的大文件结构,一个xxx.py就是一个模块,模块名就是.py文件名xxx,多个模块在同一个文件夹下,其文件夹的根目录创建了__init__.py,则当前目录称为包packet,文件名就是包名。
// 当有python程序要调用某一单独模块时,需要用import申明,如:
import outHello
// 当调用某一包中的程序时,需用from...import申明:
from company import hello //company是包名
from company import hi
// 申明了包之后,若调用模块的函数需要用moduleName.functionName格式:
hello.test()
hi.printGenerator()
outHello.printModuleName()
提示:在命令行执行时,仅仅敲击python testModule.py即可,不用跟模块文件,这点不同于C程序的编译,其中testModule.py是模块的测试程序。
(2)面向对象编程Object Oriented Programming,OOP,其把对象作为程序的基本单元,对象包括数据和操作数据的函数。两大概念:类Class,实例Instance,类是实例的模板,实例是具体的对象
class Student(object): //类名Student的首字母一般大写,object是继承的类,所有的类都会继承object
def __init__(self, name, score): //__init__方法用于定义属性,第一个参数self永远不变
self.__name = name //变量前面加__标识这是私有变量(只能在前面加,后面不能有),不能从外部直接访问
self.__score = score
def print_score(self): //类的方法的参数第一个永远是self
print("%s:%s" %(self.__name, self.__score))
def get_grade(self):
if self.__score >= 90:
return 'A'
elif self.__score >= 60:
return 'B'
else:
return "C"
yi = Student('yi', 59) //创建一个Student类的对象yi,传入name和score参数
miao = Student('miao', 89)
yi.print_score() //调用类的方法
miao.print_score()
print(miao.get_grade())
(3)继承和多态:父类Base class,子类Subclass
class Animal(object): //Animal是父类
def __init__(self):
pass
def run(self):
print('Animal is running...')
class Dog(Animal): //Dog是继承自Animal的父类
def run(self):
print('Dog is running...')
def run_twice(animal):
animal.run()
run_twice(Animal()) //输出:Animal is running...
run_twice(Dog()) //输出:Dog is running...
//多态:同样的run_twice()方法,可接受不一样的类赋值,不一样的输出结果
dog = Dog()
print(isinstance(dog, Animal)) //输出:True
//使用isinstance方法判断子类对象与父类的关系
(4)实例属性和类属性
class Student(object):
count = 0 //这个是类本身的属性,类的实例可以访问,但本身并不拥有
def __init__(self, name):
self.name = name //这个是实例属性
Student.count += 1
print(Student.count) //输出:0
bart = Student('Bart')
print(Student.count) //输出:1
print(bart.count) //输出:1
(5)异常try…except…finally
try:
print('try...')
r = 10 / int('0') //int()函数将字符串转为整形
print('result:', r)
except ValueError as e: //e是固定的
print('ValueError:', e)
except ZeroDivisionError as e: //ZeroDivisionError会在这里被捕获
print('ZeroDivisionError:', e)
else:
print('no error!')
finally: //finally会一直被执行
print('finally...')
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e: //try...except可以跨越多层调用捕获错误,
print('Error:', e) //main里将捕获foo的错误,Exception为顶级错误类型,
finally: //涵盖各类错误
print('finally...')
main()
提示:所有的错误类型都继承自BaseExcep,继承关系见
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
(6)错误栈:会导致程序执行中断,而异常不会
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main1():
bar('0')
main1()
执行结果:
Traceback (most recent call last):
File "new.py", line 647, in <module> //先定位模块
main1()
File "new.py", line 645, in main1 //定位起始函数
bar('0')
File "new.py", line 642, in bar //定位中间函数
return foo(s) * 2
File "new.py", line 639, in foo
return 10 / int(s) //定位问题点,并打印错误信息
ZeroDivisionError: division by zero
(7)利用python内置logging模块,嵌套使用异常try…except和错误栈
import logging
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main2():
try:
bar('0')
except Exception as e:
logging.exception(e) //技能捕获异常,也能打印错误栈
main2()
(8)使用raise抛出错误
def foo(s):
n = int(s)
if n == 0:
raise ValueError('invalid value: %s' %s) //抛出错误,由其上层调用捕获
raise ValueError //也可以无打印信息
return 10/n
def bar1():
try:
foo('0')
except ValueError as e: //捕获错误,并打印出错信息
print('ValueError!')
raise //再次抛出错误,给当前函数的上层调用
bar1()
第五部分,python调试和单元测试
(1)代码调试:打印和断言
def foo(s):
n = int(s)
assert n != 0, 'n is zero'
//断言一般用于参数检查,如传值检查,除数检查
//断言抛出AssertionError错误,
//使用python -O new.py,可以关闭代码中的assert
return 10 / n
def main():
foo('0')
main()
(2)代码调试:logging
import logging
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.DEBUG)
s = '0'
n = int(s)
logging.info('n = %d' %n)
logging.debug('n = %d' %n)
print(10/n)
//使用logging调试是调试的终极方法,尤其用于大程序
//具体参考https://www.cnblogs.com/CJOKER/p/8295272.html
提示:ubuntu日志参考https://www.cnblogs.com/EasonJim/p/7189491.html
(3)单步调试:pdb
import pdb
s = '0'
n = int(s)
pdb.set_trace() //程序执行到此为停下,并进入pdb调试环境
print(10 / n)
pdb常用命令:
l -- 查看代码
n -- 下一步
c -- 继续执行
p xxx -- 打印变量值
q -- 退出
(4)单元测试
student.py: //运行单元命令为python -m unittest student
#!/usr/bin/env python
import unittest //python自带unittest模块用来实现单元测试
class Student(object): //需要测试的类
def __init__(self, name, score):
self.name = name
self.score = score
def get_grade(self):
if self.score >= 80:
return 'A'
elif self.score >= 60:
return 'B'
else:
return 'C'
class TestStudent(unittest.TestCase): //测试类,必须从unittest.TestCase继承
def setUp(self): //setUp()和tearDown()方法会在每一个测试方法
print('setUp...') //的前后分别执行,用于打开和关闭数据库
def tearDown(self):
print('tearDown...')
def test_80_to_100(self): //所有测试方法必须以test开头,不然无法执行
s1 = Student('yi', 90)
s2 = Student('miao', 89)
self.assertEqual(s1.get_grade(), 'A')
//assertEqual()是最常用的断言,判断s1.get_grade()和'A'是否相等
//assertTrue()判断括号内是否为真
self.assertEqual(s2.get_grade(), 'A')
def test_60_to_80(self):
s1 = Student('yi', 60)
s2 = Student('miao', 79)
self.assertEqual(s1.get_grade(), 'B')
self.assertEqual(s2.get_grade(), 'B')
with self.assertRaises(AttributeError): //使用assertRaises抛出指定类型的Error
value = s1.age //这里抛出属性错误
提示:单元测试代码要非常简单,如果测试代码太复杂,那么测试代码本身就可能有bug。
第六部分,python文件IO和正则表达式
(1)文件读写
try: //使用try...finally,不论是否打开成功,都能关闭文件
f = open('test-file/test.txt', 'r')
//r是只读;rb是读取二进制文件(如图片)
print(f.read())
//read()一次读完所有内容,read(size)一次读size大小内容
finally:
if f:
f.close()
with open('test-file/test.txt', 'r') as f: //with语句会自动调用close(),代码更简洁
for line in f.readlines(): //readlines()一次读取所有内存,并按行返回
print(line.strip())
with open('test-file/test.txt', 'w') as f: //w是直接覆盖写;a为追加;
f.write('hello world')
(2)操作文件和目录:使用python内置os模块
import os
print(os.name)
//获取操作系统类型
print(os.uname())
//获取系统信息
print(os.environ.get('PATH'))
//获取系统环境变量
print(os.path.abspath('.'))
//获取当前路径
print(os.path.join('/mnt/hgfs/ycaoshare/python', 'testdir'))
print(os.path.split('/mnt/hgfs/ycaoshare/python/testdir'))
//创建和分离文件夹或文件
os.mkdir('/mnt/hgfs/ycaoshare/python/testdir')
os.rmdir('/mnt/hgfs/ycaoshare/python/testdir')
//创建或删除文件夹
os.rename('test.txt', 'ycao.txt')
os.remove('test.txt')
//更名或删除文件
print([x for x in os.listdir('.') if os.path.isdir(x)])
//列出当前目录的所有文件夹
print([x for x in os.listdir('.') if os.path.isfile(x) and os.path.splitext(x)[1]=='.py'])
//列出当前目录所有.py文件
提示:复制文件为shutil模块的copyfile()函数
https://docs.python.org/3.6/library/shutil.html?highlight=copyfile#shutil.copyfile
(3)正则表达式
正则表达式是专门用来匹配字符串的工具,其采用一种描述性的语言给待匹配的字符串定义规则,用以匹配合乎规则的字符串,规则如下:
\d --数字
\w --字母或数字
. --所有字符
* --任意个字符
+ --至少一个字符
? --0或1个字符
{n} --n个字符
{n,m} --n到m个字符
\s --一个空格或tab
\- --短横字符
\_ --下划线字符
[] --用来限制匹配范围,使更精准
A|B --匹配A或B
^ --代表行首
$ --代表行尾
0-9 --精准匹配数字
a-z --精准匹配小写字母
A-Z --精准匹配大写字母
@ --精准匹配@
\, --逗号字符
\; --分号字符
() --用来提取子字符串(group)
import re //re模块提供正则表达式功能
print(re.match(r'^\d{3}\-\d{3,8}$', '010-12345'))
//建议使用前缀r'',具体不完全清楚??
//匹配成功,返回<_sre.SRE_Match object; span=(0, 9), match='010-12345'>
//匹配失败,返回None
常用判断方法:
test = '010-12345'
if re.match(r'^\d{3}\-\d{3,8}$', test):
print('ok')
else:
print('failed')
切分字符串:
print(re.split(r'\s+', 'a b c')) //能区分连续空格
print(re.split(r'[\s\,]+', 'a,, b \ c'))
print(re.split(r'[\s\,\;\_]+', 'a,, b __c'))
提起字符串:
m = re.match(r'^(\d{3})-(\d{3,8})$', '010-12345')
print(m.groups()) //打印所有分组
print(m.group(0)) //打印原始字符串
print(m.group(1)) //打印第一个分组
print(m.group(2)) //打印第二个分组
匹配时间的正则表达式:
t = '14:05:30'
m = re.match(r'^(0[0-9]|1[0-9]|2[0-3])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])\:(0[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])$', t)
print(m.groups())
re_telephone = re.compile(r'^(\d{3})-(\d{3,8})$')
//预编译正则表达式,方便重复使用,以提高效率
print(re_telephone.match('010-12345').groups())
print(re_telephone.match('010-88993').groups())
第七部分,python进程和线程
待补充。。。
总结
python的语法基本讲完了,但是想熟练掌握python需要足够的代码量。
google python语法:https://zh-google-styleguide.readthedocs.io/en/latest/google-python-styleguide/contents/