python学习笔记

python学习笔记

前言

  • 本文是学习从python的心得整理的.
  • 本文适合有一定编程基础,从其他语言学习python的人.对于其他语言通用的知识点一带而过.python特有的知识点着重介绍,同时也会比较python和其他语言的异同.
  • 版本基于python3
  • 本文重点在于介绍python的规则和写法,对于库的使用(如网络,IO)不做介绍

python基础

基础

  • python是动态语言(解释型语言),代码后缀为py,第一次运行会产生预编译的文件,后缀为pyc
  • python的优点是:可读性强,使用便利(很多库都隐藏细节,一句话调用),生态强大(支持与其他语言的黏合,社区活跃模块多),小巧且支持多平台(多个os内置python,写一个脚本可以直接运行)
  • 大小写敏感
  • 区分代码结构不用{},也不用begin,end,代码的结构完全靠缩进,缩进可以用tab,也可以空格,这没有统一规定,但是大部分人约定用4个空格作为缩进.
  • 空语句用pass
  • 注释是#,大段注释用```
  • 一行代码太多写不下,可以写到多行,然后行尾用" "连接

运算符

  • 运算符:+加-减*乘/除,%求余,//取整除,**幂
  • 位运算:&与,|或,^异或,-非,<<左移,>>右移
  • 比较:==,!=,<>(是的,python的不等于有两种写法),>,<,>=,<=
  • 逻辑运算:and并且,or或者,not非
  • 两个引用是否是一个对象:is,is not
  • 是否在/不在集合内:in,not in,eg:if ITEM in COLLECTION:,对于list和tuple,检查ITEM是否在集合内,对于map,检查ITEM是否在map的key中。

数据类型

  • 数字:包括int(123),long(123L),float(1.2),complex(1+2j).
  • 文本:用"",或者''都可以.大段文本可以用'''.python的文本格式化有多种写法
    • 方法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"差不多,都是无视字符串中的转义字符,在拼接代码和写正则的时候很有用.
  • 元组tuple:通过('a','b')定义,不可变.可以转化为list和字符串
  • 列表list:通过['a','b']定义,可以添加删除元素,也可以通过特定方法在头尾删减元素,把它当作栈或者队列使用.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.同样最后那个不算.所以最后输出两个.)
  • 字典map:通过x={'a':1,'b':2}定义,通过x['a']引用
  • 集合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:
    DoSomething
######################
for item in list:
    DoSomething
else
    DoSomething
######################
for index in range(len(list)):
    DoSomething

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 删除列表,map,字符串等数据结构中的元素.例如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) #指定参数的名字,此时可以按照任意顺序写参数

#多个返回值
def func2(a,b):
    return a,b

#调用
a,b = func2(1,3)

#限定类型(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:
# **map参数,可以输入一个字典.
def func2(arg1,**map1):
    print(map1['a'])#1
    print(map1['b'])#xxx
    print(map1) #{a=1,b="xxx"}

#调用
func(m,a=1,b="xxx")

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


def func1(arg1, *tuple1):
    print(tuple1)
a=(1,2,3,4,5)
#调用
func(a) #报错
func(*a) #输出(1,2,3,4,5)

def func2(arg1,**map1):
    print(map1)

#调用
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),
  • 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()
  1. 这段代码首先执行f2(),
  2. f2中生成了一个新的name变量
  3. 打印name,这时候输出"f2专属",最后返回f1
  4. ret被设置为f1,然后执行f1
  5. 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](https://my.oschina.net/flyinghawk)
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](https://my.oschina.net/u/2352753)
[@hello2](https://my.oschina.net/u/2517253)
def foo():
    do something

#################
#综合例子
#这个例子中,装饰器可以接受参数
#相当于foo=hello3(arg1,**map1)(foo(参数))
#因为装饰器返回的只能是void func(f)这种类型,不能接受其他参数,所以在需要接受参数时,需要额外包一层,在最外层获取参数
def hello3(arg1,**map1):#我负责接收参数
    def realHello3(func):#我才是真正的装饰器
        def wrapper(para1):#我是装饰器里嵌套的函数
            print(map1["mapKey"])
            print(arg1)
            return func(para1)+", foo has been extended"
        return wrapper
    return realHello3

@hello3(arg1="参数",mapKey="字典参数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

(与此对应,对于类成员方法有@classmethod的装饰器)

但是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提出的鸭子测试,“鸭子测试”可以这样表述:

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

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

关于类的其他

  • 判断示例的类型
    1. Type t= type(实例)
    2. Boolean b=isinstance(实例,类)
    3. 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 包名".

字符串常用函数

len(str) #获取长度
str=str.strip() #trim(默认删除空格,有参数删除参数指定的字符)
str=str.replace() #替换
str=",".join(list) #以,为分隔符,把list的每个字符串join起来.

正则表达式

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) #要处理的文本文本

命名规范

module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_VAR_NAME, instance_var_name, function_parameter_name, local_var_name.

转载于:https://my.oschina.net/somereasons/blog/909862

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值