深入理解python.md_python_learning: python的学习笔记,正文就放在readme.md里了.以后用的多了体会更深的时候就来更新一下,可以追溯自己对python的理解过程....

python学习笔记

前言

本文是学习从python的心得整理的.

本文适合有一定编程基础,从其他语言学习python的人.对于其他语言通用的知识点一带而过.python特有的知识点着重介绍,同时也会比较python和其他语言的异同.

版本基于python3

本文重点在于介绍python的规则和写法,对于库的使用(如网络,IO)不做介绍

python基础

基础

python是动态语言(解释型语言),代码后缀为py,第一次运行会产生预编译的文件,后缀为pyc

python的优点是:可读性强,使用便利(很多库都隐藏细节,一句话调用),生态强大(支持与其他语言的黏合,社区活跃模块多),小巧且支持多平台(多个os内置python,写一个脚本可以直接运行)

python的主要版本包括2,3,其中3对语法有很大的改变,更贴近现代语言,因此2和3的代码不兼容.这也造成了python的分裂.

大小写敏感

区分代码结构不用{},也不用begin,end,代码的结构完全靠缩进,缩进可以用tab,也可以空格,这没有统一规定,但是大部分人约定用4个空格作为缩进.

空语句用pass

注释是#,大段注释用```

语言中的空用None表示

运算符

运算符:+加-减*乘/除,%求余,//取整除,**幂

位运算:&与,|或,^异或,-非,<>右移

比较:==,!=,<>(是的,python的不等于有两种写法),>,=,<=

逻辑运算:and并且,or或者,not非

两个引用是否是一个对象:is,is not

是否在/不在集合内:in,not in,eg:if ITEM in COLLECTION:,对于list和tuple,检查ITEM是否在集合内,对于dict,检查ITEM是否在dict的key中。

数据类型

数字

包括

整数 int(123)

长整数 long(123L)

浮点数 float(1.2)

复数 complex(1+2j)

文本

用"",或者''都可以.大段文本可以用'''.

文本格式化:

方法1:老写法,支持python2,和C语言类似."我叫%s, 今年%d岁!" % ('小明', 10)

方法2:新写法,"字符串{0}".format("数字字符串都可以")

"{} {}".format("hello", "world")----'hello world'

"{0} {1}".format("hello", "world")----'hello world'

"{1} {0} {1}".format("hello", "world")----'world hello world'

"网站名:{name}, 地址 {url}".format(name="google", url="www.google.com"),其中name="google", url="www.google.com"这部分,也是实现定义的一个字典.

和C#类似,大括号中也可以使用格式化字符串,"{0:.2f}".format(1.234567)----1.23,注意".2f"都是格式化字符串.

r"abc"和C#中的@"abc"差不多,都是无视字符串中的转义字符,在拼接代码和写正则的时候很有用.

字符串常用函数:

len(str) #获取长度

str=str.strip() # trim(默认删除空格,有参数删除参数指定的字符)

str=str.replace() # 替换

str=",".join(list) # 以,为分隔符,把list的每个字符串join起来.

str.isdigit(),str.isdecimal(),str.isnumeric() # 判断是否是数字

元组tuple

通过('a','b')定义,不可变.可以转化为list和字符串

列表list

和js一样,list是一个非常宽泛的概念,可以当作数组,列表,队列,栈使用.通过['a','b']定义.类型关键字为list.

python的列表还支持快捷的子列表的写法.如在a=[1,2,3,4,5]的时候

a[1:4] = [2,3,4].(从0开始的索引,第1个到第2个.最后那个数字不算)

a[2:] = [3,4,5].(从第2个到最后.)

a * 2 = [1,2,3,4,5,1,2,3,4,5].(重复2次)

a + a = [1,2,3,4,5,1,2,3,4,5].(叠加)

a[-3,-1] = [3,4].(倒数第三个,到倒数第一个,负数的话,从后往前数,索引从-1开始.也就是-1对应5,-2对应4,-3对应3.同样最后那个不算.所以最后输出两个.)

字典dict

通过x={'a':1,'b':2}定义,注意,python的字典叫dict,不像java叫map

引用方式,

通过x['a'],此时,如果key中没有a,会抛出异常.

通过x.get('a'),此时,如果key中没有a,会返回None

出了containKey方法,其他语言字典常用的功能都有.而且python字典的key也可以遍历.

集合set

另一个极富特色的类型,可以用于科学计算.通过{1,2,3}定义,集合可以通过|,&,-,^实现并,交,差,异或的运算.还可以用过x in {1,2,3}测试元素是否在集合中存在.集合中没有重复的元素.

条件

关键字:if,elif,else,注意if的结尾要有冒号.

python没有switch case语句.

if num=5:

DoSomething

elif num=6:

DoSomething

else:

DoSomething

三元运算符

写法和其他语言很不一样,格式为:为真时的结果 if 判定条件 else 为假时的结果

例如:num = 1 if x>3 else 0

循环

循环支持while和for

while 条件:

DoSomething

#break

#continue

else:

pass

######################

for item in list:

pass

else

pass

######################

for index in range(len(list)):

pass

#也可以这样写

for i, item in enumerate(list):

pass

python循环中有个很特别的概念,是else.含义为:在循环条件为false的时候执行.也就是说,通常,在循环结束后,执行一次.但是,如果循环是被中断的,那么else不会被执行.

循环还有一个特殊的写法:print([x ** 2 for x in range(20) if x > 4])相当于C# linq的,from x in list where x>4 select x^2

特殊运算符

del 删除列表,dict,字符串等数据结构中的元素.例如del a[3],del a[1:5]

函数

通用

基本写法如下

#基本写法

def func(para1,para2=默认值):

DoSomething

return Something

#调用

func(a,b)

func(a) #para2不用填写,有默认值

func(para2=b,para1=a) #指定参数的名字,此时可以按照任意顺序写参数

#限定类型(python管这个叫注解,而java中的注解对应python的装饰器)

def foobar(a: int, b: "it's b", c: str = 5) -> tuple:

return a, b, c

变长参数

由于python强大的字典,tuple功能.多返回值不在话下,甚至对于传入参数,都有很多特色的写法.

#变长参数

#C#里写法是void func(param int[] args),java里写法是void func(int... args)

#写法1:

# *tuple参数,可以输入一个tuple,调用的时候看作数组就行.

def func1(arg1, *tuple1):

print(tuple1)

#调用

func1(1,2) #输出(2)

func1(1,2,3,4) #输出(2,3,4)

#写法2:

# **dict参数,可以输入一个字典.

def func2(arg1,**dict1):

print(dict1['a'])#1

print(dict1['b'])#xxx

print(dict1) #{a=1,b="xxx"}

#调用

func(m,a=1,b="xxx")

注意,虽然调用函数时,变长参数被看作tuple和dict,并不意味你可以把tuple和dict当作参数直接输入过去,如果要在参数中使用tuple和dict当作不定参数,需要使用*和**运算符解包.

def func1(arg1, *tuple1):

print(tuple1)

a=(1,2,3,4,5)

#调用

func(a) #报错

func(*a) #输出(1,2,3,4,5)

def func2(arg1,**dict1):

print(dict1)

#调用

m={a=1,b="xxx"}

func(2,a=1,b="xxx") #可以

func(2,m) #报错,必须解包

func(2,**m) #字典已经解包,可以执行.

lambda

lambda是匿名函数.当函数很短的时候,不需要规规矩矩的写个def xxx():,而是写个lambda就行了,可以减少一些代码量,很多现代语言都支持了这个特性.

由于python是弱类型,不像C#那样可以明确知道正在定义的是delegate,也不像java那样可以明确的知道正在定义的是函数接口,在定义lambda时,需要明确使用lambda关键字(充分体现了python关键字满天飞的思想.)

写法:sum = lambda arg1:arg2:arg1+arg2

调用:sum(1,2)

类似linq的写法

对于操作数组,C#有linq,java有stream,python有几个函数可以实现类似linq的效果

map函数:Iterable2=map(func1,Iterable1),把Iterable中的每一个元素都执行一次func1,然后输出为新的Iterable,func1的原型是object func1(object obj1),例子:new_list=list(map(lambda: x : x*2,old_list)),注意map处理后的返回值是map类型,因此要用list变回列表。

reduce函数:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4),可以用在数组求和的场合.

fiter函数:Iterable2=filter(func1,Iterable1),把符合filter条件的存入Iterable2中,func1的原型是:boolean func1(object obj)

sorted函数:sorted函数是对列表排序,然后输出到新列表中,并不改变原来的列表。Iterable2=sorted(Iterable1),直接排序;如果排序前要对列表处理,用key参数,Iterable2=sorted(Iterable1,key=func),func原型object func1(object obj1),返回值为排序的那个字段;如果需要反序,用第三个参数reverse=True,(脑洞太大..和别的语言习惯很不一样,别的语言习惯传入一个比较函数,确定两个元素的比较方式.)

sort函数:sort是对列表直接排序,并改变列表本身;Iterable1.sort(),参数的用法和sorted一致。

l.sort()#默认排序

l.sort(reverse=True)#反向排序

def sort_key(s)

return s.xxx #或者s.lastindex之类的

l.sort(key=sort_key)#用sort_key的规则排序

l.sort(key=lambda a:a.xxx)#简化一下,用lambda代替规则函数。

l1=sorted(l,key=sort_key)#sorted示例,此时l内容不变,用法和sort基本一样。

函数的动态特性

python是解释性语言,一个重要特点是,函数也可以作为变量.

def add(x, y, f):

return f(x) + f(y)

还有一个特点是,可以在函数内部定义函数,如

def str2int(s):

def fn(x, y):

return x * 10 + y

def char2num(s):

return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]

return reduce(fn, map(char2num, s))

变量作用域和闭包

python有两级作用域,函数和模块.相比之下,java和C#还有更小的一级,就是块作用域(比如for(int i=0;;)或者using(xxx x=new xxx())).python有个概念叫作用域链,也就是说,一个变量在本作用域中找不到,就会向上找.这一点和其他语言都是一样的.只不过在函数嵌套函数的情况下,会有些拗口,看下面的例子

name = '模块级变量'

def f1():

print(name)

def f2():

name = 'f2专属'

print(name)

return f1

ret = f2()

ret()

这段代码首先执行f2(),

f2中生成了一个新的name变量

打印name,这时候输出"f2专属",最后返回f1

ret被设置为f1,然后执行f1

f1开始执行,虽然f1是从f2返回的,但是f1和f2毕竟是平级,此时f2已经结束.所以f2的那个冒牌name='f2专属'已经没了.于是向上找name,找到"模块级变量",并打印

由此可见,python和其他语言一样遵守向上一级找变量的特点,即使是函数经过多次嵌套,只要把握住函数运行时,有哪些变量存在,还是能轻松跟踪变量的变化.类似的特性,在js上叫做链式作用域.

注意:可能有人不会同意f2的name是新创建的,为此特意写了js代码验证:

var name = 'zhang'

function f1(){

console.log(name);

}

function f2(){

var name = 'eric';

console.log(name);

return f1;

}

ret = f2();

ret();

写var name='eric'的时候,输出eric,zhang;写name='eric'的时候,输出eric,eric

装饰器

python的装饰器是给函数,类的前面加@注解,从而实现一些功能.和C#和Java的注解非常类似.原理上就不一样了.先看例子

def hello(fn):

def wrapper():

do something

fn()#执行一下fn(),不执行也行.

do something

return wrapper;

@hello

def foo():

do something

通过这种方式,运行foo()的时候,不仅会运行foo的代码,还会运行wrapper的代码.实际上,加了注解之后,实际相当于执行了foo=hello(foo),foo作为参数被送入hello中,然后执行wapper中的代码,最后,foo变成了wapper(此时看foo的__name__,发现它确实变了.).那么执行foo(注意这是改变之后的foo),相当于执行wapper里的代码.

题外话:这段代码涉及到链式作用域和闭包,没理解的可以往前看.

知道了原理,就可以有更多花样.

#相当于foo=hello1(hello2(foo))

@hello1

@hello2

def foo():

do something

#################

#综合例子

#这个例子中,装饰器可以接受参数

#相当于foo=hello3(arg1,**dict1)(foo(参数))

#因为装饰器返回的只能是void func(f)这种类型,不能接受其他参数,所以在需要接受参数时,需要额外包一层,在最外层获取参数

def hello3(arg1,**dict1):#我负责接收参数

def realHello3(func):#我才是真正的装饰器

def wrapper(para1):#我是装饰器里嵌套的函数

print(dict1["dictKey"])

print(arg1)

return func(para1)+", foo has been extended"

return wrapper

return realHello3

@hello3(arg1="参数",dictKey="字典参数1")

def foo(para):

return "I am foo, my para is " + para

print(foo("啦啦啦"))

此外,装饰器还可以用一个类实现.

class myDecorator(object):

def __init__(self, fn):

print( "inside myDecorator.__init__()")

self.fn = fn

def __call__(self,para):

self.fn(para)

print("inside myDecorator.__call__()")

@myDecorator

def aFunction(para):

print("inside aFunction({0})".format(para))

print("Finished decorating aFunction()")

aFunction("my string")

# 输出:

# inside myDecorator.__init__()

# Finished decorating aFunction()

# inside aFunction(my string)

# inside myDecorator.__call__()

python的模块

模块是动态语言很重要的概念.对代码的管理并不一定要通过类,也可以通过模块进行.和类试图描述现实世界的方式不同,模块就是一些具有一定功能的代码.想用的时候就引进来.

模块的定义

简单来说,一个py文件就可以看作一个模块.

模块的上一级是包,一个文件夹就是一个包.但是,要定义包,光有文件夹是不够的,要在文件夹中放置一个文件,init.py,告诉编译器这是一个包.这个文件可以是空的,也可以放置一些代码,在包初始化的时候执行.

有了包的概念之后,引用包的方式为"包名.模块名".

包的结构可以嵌套如package1.package2.module.

模块和程序

每个py都是一个模块,同时也是一段程序,那么在运行py文件的时候(方式为python xxx.py),如果想指定入口函数(也就是别人家的main函数),需要在py文件中加入if name=='main'.

如果在命令行中运行python xxx.py,解释器会将xxx.py的__name__属性设置为'main',利用这一点可以设置入口函数.如果xxx.py是被其他包引用的.则__name__属性不对,入口函数也不会执行.

def test():

print("程序开始运行了")

if __name__=='__main__':

test()

模块的引入

有两种方式:

import module1

from module1 import *

使用方式1,要使用module1里的函数,要写module1.func();使用方式2,要使用module1例的函数,直接写func().

另外对于方式1,如果引入包,名字要写全,比如

import package1.module1

使用的时候需要写package1.module1.func1(),为了避免麻烦,可以使用别名,写成

import package1.module1 as short1

使用的时候写short1.func1()就可以了.

使用dir()函数可以查看一个模块的所有函数和变量

作用域

这是python比较混乱的地方.但基本原则是.前面带下划线的,表示是内部变量,不可以引用.

如果模块是用import module1方式引用的.那么使用module1._func()不会有任何提示,此时依靠开发人员自觉.不要去引用内部函数.

如果是用from module1 import *方式引用的.那么引用_func()会报错.

对于from module1 import *方式引用的模块,如果module1中定义了变量__all__,那么变量中列出的是公开函数,没有列出的私有函数,此时不受下划线规则的限制.即使是下划线开头的函数,被列入了__all__数组中,也可以被外面引用.见例子

__all__ = ['_private_variable', 'public_teacher']

public_variable = "Hello, I am a public variable." #不能访问

_private_variable = "Hi, I am a private variable."#在__all__中,可以访问

def public_teacher():#在__all__中,可以访问

print "I am a public teacher, I am from JP."

def _private_teacher(): #不能访问

print "I am a private teacher, I am from CN."

#再次提醒这种限制对于import module1引入的情况下无效

python的类

动态语言通常不用类,像php和js就在没有类的情况下裸奔的很多年,直到201X年才加入类的概念.同样,类对python也不是必须的.但不可否认,类确实是描述现实世界的好方法.python也从一开始就提供了类的支持.

类的定义和使用

class myClass1:

var1 = 0 #静态变量

def __init__(self):#构造函数

pass

def func(self,para1):#成员函数

pass

a = myClass1()

print(myClass1.var1)

a.func(para1)

类的所有成员函数的第一个参数都必须是类本身,通常约定这个参数叫self

静态变量和成员变量

和js类似,python没有显著的静态变量的定义,如果硬要定义的话,可以这样写

class miaomiaomiao:

sss = 45 #static变量

def __init__(self):

self.nn = 4 #类实例的变量.

@staticmethod

def statisticFunc():#静态方法,通过添加注解

pass

但是python的静态变量和java的非常不同.python的静态变量,实例也是可以访问的.比如

print(miaomiaomiao.sss) #输出45,可以被类调用,体现了静态语言static变量的特性

c1=miaomiaomiao()

c2=miaomiaomiao()

print(c1.sss)#输出45,类的实例也可以调用sss

c1.sss=100

print(c1.sss)#输出100,c1的sss被改变了.

print(c2.sss)#输出45,刚才的赋值只改变了c1的sss,没有改变类的sss,也没有改变c2的sss

print(miaomiaomiao.sss)#输出45

另外作为动态语言,python的实例变量,是可以在运行中添加或者删除.比如

a=miaomiaomiao()

a.xx=3 #添加

del a.nn #删除

同理,类成员的函数,也是可以在运行中动态添加和删除的.但是添加函数,如果加到实例上,另一个实例是不能用这个函数的(类似于给js的类添加函数,没有加到prototype上.)

成员的公开性

对于类的属性,一个下划线开头是protect,两个下划线开头是private,否则是public

对于类的方法的公开性,可以遵守属性的规则,另一种方式是显式在名字上注明__private_func(),__internal_func(),__protected_func()

继承和多态

class parent:

def funcP():

pass

#在class中指定父类即可

class child(parent):

def funcP():#覆盖父类的方法,不用指定override

pass

#调用

child1=child()

child1.funcP()

###################################

#多重继承

class childs(parent1,parent2):

由于python是动态语言,所以也遵循鸭子类型的概念,只要类中有特定的函数或者变量,就可以被看作某个类的子类.

在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,“鸭子测试”可以这样表述:

“当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。”

在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数接受的这种行为引出了以上表述,这种决定类型的方式因此得名。

关于类的其他

判断示例的类型

Type t= type(实例)

Boolean b=isinstance(实例,类)

Boolean b=hasattr(实例,属性名)#还有getattr,setattr

限定类的成员:给类加上变量__slots__,例如__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称,只允许添加name和age两个属性.

str(self):这是python版本的toString()函数,

iter(self),next(self):迭代器,可以使类支持for in ,通常内容是return self

getitem(self,n):使得类支持通过下标获取第n个元素.

枚举

早先python没有枚举,3.4版本才加入进来。之前一直用各种手段实现,虽然麻烦但也带来了好处,可以写各种自己想要的枚举,比如带数字的,带描述的。3.4之后加入的枚举也是一种实现而已。如果3.4之前的版本想要用标准的枚举,可以单独安装,pip install enum34。

用法

import enum

#枚举是通过实现一个继承于Enum的类实现的。别的语言好像也这么干,所以没毛病。

class animals(enum.Enum):

monkey = 11,

horse = 22,

turkey = 32,

ox = 43

异常处理

#最简单写法

try:

pass

except BaseException as e:

print(e)

#复杂一些的

try:

print("try")

except ValueError as e:#不同类型的异常

print("ValueError")

except BaseException as e:#基础异常要放在最下面

print("BaseException")

else:#没有错误的时候会执行这里

print("else")

finally:#有没有错误都会执行这里

print("finally")

自定义异常:让一个类继承某个异常类就可以.

抛出异常:raise SomeError("消息")

python内置了logging库,可以记录日志

其他

概述

python的一大优势是拥有包管理器,如同安卓有了应用市场,C#有了nuget,node有了npm,让包的安装和引用非常便利.还可以解决依赖项的难题.python包管理的应用很多,官方已经内置了pip工具.安装时注意选择即可.基本使用方式为"pip install 包名".

正则表达式

import re

str="要处理的文本"

reee=re.compile(r"正则") #编译正则

re.match(r"正则",str) #匹配整个str

h=re.search(r"正则",str) #查找str中第一个匹配,h是match对象

for h in re.finditer(r"正则",template_str):#查找str中的所有匹配,并挨个处理,其中h是match类型.

pass

re.sub(r"正则",代替,str) #替换str中的匹配

match对象的成员:

group(number) 第几个group的文本,其中,整个匹配内容是group(0),然后每一个分组(也就是括号)可以通过group(N)查询到

start(number) 第几个group的开始,其中number是分组编号.

end(number) 第几个group的结束,其中number是分组编号.

sub函数非常有意思,替换功能非常强大.关键就在第二个参数代替上.这个参数可以是文本,也可以是函数.函数会用代替参数的内容,替换整个正则的匹配.

当它是文本时,代表要替换的文本.这个文本是可以格式化的.比如"\1somthing\3",其中\1的位置会用正则的分组1(group(1)的内容)代替,\3的位置会用分组3的内容代替.这就使得灵活性非常高.

当它是函数时,可以传入一个函数引用.形式为def xxx(e):,然后返回值是一个字符串.sub函数会用返回的字符串代替查找到的整个匹配.

import re

str="要处理的文本"

#r"(文)本"匹配之后,group(0)是文本,group(1)是文

re.sub(r"(文)本","呵呵呵",str) #要处理的呵呵呵

re.sub(r"(文)本","~\\1~",str) #要处理的~文~

re.sub(r"(文)本",lambda x:x.group(0)*2,str) #要处理的文本文本

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值