Python基本概念
python基本概念
环境变量设置
- 启动终端Terminal;
- 进入当前用户的home目录,输入cd~;
- 先尝试打开open .bash_profile如没有,则新建,touch .bash_profile;
- 打开open .bash_profile编辑;
alias python2=’/usr/local/bin/python2.7’
alias python3=’/anaconda3/lib/python3.7’
alias python=python3
- 保存关闭后,source ~/.bash_profile
python的一些特点
支持面向对象、开源
面向对象是:
把数据及对数据的操作方法放在一起,作为一个相互依存的整体——对象。
对同类对象抽象出其共性,形成类。类中的大多数数据,只能用本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定
面向过程就是:
自顶向下顺序执行,逐步求精;其程序结构是按功能划分为若干个基本模块,这些模块形成一个树状结构;各模块之间的关系尽可能简单,在功能上相对独立;每一模块内部均是由顺序、选择和循环三种基本结构组成;其模块化实现的具体方法是使用子程序。程序流程在写程序时就已决定
Python是解释型语言、运行速度慢,用户感知不到
高级语言,代码不能加密
第三方库比较丰富
适合开发的场景:网络应用,包括网站、后台服务等等、许多日常需要的小工具,包括系统管理员需要的脚本任务等等
数据类型和变量
数据类型:整数、浮点数、字符串、布尔值、空值None
变量
常量(PI=3.14159265359)
字符串和编码
*编码问题:
最先有美国的ASCII,中国的GB2312、日本的Shift_JIS、韩国的Euc-kr…
后来统一成Unicode,所有语言都支持,但是内存大
大多数英文编码的使用UTF-8、节约空间
格式化:
ord()函数获取字符的整数表示
chr()函数把编码转换为对应的字符
基础
list
Python内置的一种数据类型是列表:list。list是一种有序的集合,可以随时添加和删除其中的元素
tuple
另一种有序列表叫元组:tuple。tuple和list非常类似,但是tuple一旦初始化就不能修改
循环
for x in …循环就是把每个元素代入变量x,然后执行缩进块的语句。
如果要计算1-100的整数之和,从1写到100有点困难,幸好Python提供一个range()函数,可以生成一个整数序列,再通过list()函数可以转换为list。比如range(5)生成的序列是从0开始小于5的整数
// An highlighted block
list(range(5));//[0, 1, 2, 3, 4]
Dict
请务必注意,dict内部存放的顺序和key放入的顺序是没有关系的。
和list比较,
dict有以下几个特点:
=1、查找和插入的速度极快,不会随着key的增加而变慢=
=2、需要占用大量的内存,内存浪费多=
而list相反:
=1、查找和插入的时间随着元素的增加而增加=
=2、占用空间小,浪费内存很少=
所以,dict是用空间来换取时间的一种方法
Set
set和dict类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。
set和dict的唯一区别仅在于没有存储对应的value,但是,set的原理和dict一样,所以,同样不可以放入可变对象,因为无法判断两个可变对象是否相等,也就无法保证set内部“不会有重复元素”。试试把list放入set,看看是否会报错。
参数
函数默认参数的坑:
Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了
=定义默认参数要牢记一点:默认参数必须指向不变对象!=
为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象
命名关键字参数
参数组合
在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
=对于任意函数,都可以通过类似func(*args, **kw)的形式调用它,无论它的参数是如何定义的=
函数递归
1、递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
2、使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出
3、使用递归函数的优点是逻辑简单清晰,缺点是过深的调用会导致栈溢出。
4、针对尾递归优化的语言可以通过尾递归防止栈溢出。尾递归事实上和循环是等价的,没有循环语句的编程语言只能通过尾递归实现循环。
5、Python标准的解释器没有针对尾递归做优化,任何递归函数都存在栈溢出的问题
生成器
如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器
原理:它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束。
Iterable
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function
这些可以直接作用于for循环的对象统称为可迭代对象
可以使用isinstance()判断一个对象是否是Iterable对象:
Iterator:可以被next()函数调用并不断返回下一个值的对象称为迭代器
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
函数式编程
高阶函数:
1、变量可以指向函数、函数名也是变量、传入函数
2、把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式
Filter
1、filter()的作用是从一个序列中筛出符合条件的元素
2、由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素
3、注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list
4、注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁
5、回数是指从左向右读和从右向左读都是一样的数,例如12321,909
Sorted
1、排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小
2、如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来
3、sorted()也是一个高阶函数。用sorted()排序的关键在于实现一个映射函数
lambda:
1、关键字lambda表示匿名函数,冒号前面的x表示函数参数。
2、匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果
3、用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突
4、此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数
Decorator
1、装饰器:假设我们要增强now()函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”
2、在面向对象(OOP)的设计模式中,decorator被称为装饰模式
3、OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator
4、Python的decorator可以用函数实现,也可以用类实现
5、decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便
functools.partial:
1、简单总结functools.partial的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单
2、当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。
面向对象编程
类:
1、类是创建实例的模板,而实例则是一个一个具体的对象,各个实例拥有的数据都互相独立,互不影响;
2、方法就是与实例绑定的函数,和普通函数不同,方法可以直接访问实例的数据
3、通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节
4、和静态语言不同,Python允许对实例变量绑定任何数据,也就是说,对于两个实例变量,虽然它们都是同一个类的不同实例,但拥有的变量名称都可能不同
类属性:
1、实例属性属于各个实例所有,互不干扰
2、类属性属于类所有,所有实例共享一个属性
3、不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误
@property
1、有没有既能检查参数,又可以用类似属性这样简单的方式来访问类的变量呢?
Python内置的@property装饰器就是负责把一个方法变成属性调用的
@property广泛应用在类的定义中,可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性
Mixin
1、目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系
2、只允许单一继承的语言(如Java)不能使用MixIn的设计
ORM框架
ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。
OS
序列化:
我们把变量从内存中变成可存储或传输的过程称之为序列化,序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上,反过来,把变量内容从序列化的对象重新读到内存里称之为反序列化,即unpickling
1、Python语言特定的序列化模块是pickle,但如果要把序列化搞得更通用、更符合Web标准,就可以使用json模块。
2、 json模块的dumps()和loads()函数是定义得非常好的接口的典范。当我们使用时,只需要传入一个必须的参数。
3、但是,当默认的序列化或反序列机制不满足我们的要求时,我们又可以传入更多的参数来定制序列化或反序列化的规则,既做到了接口简单易用,又做到了充分的扩展性和灵活性。
线程
ThreadLocal:
1、ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
2、一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题
线程与进程
多线程模式通常比多进程快一点,但是也快不到哪去,而且,多线程模式致命的缺点就是任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存
分布式进程
1、在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上
2、Python的分布式进程接口简单,封装良好,适合需要把繁重任务分布到多台机器的环境下
正则表达式
用\d可以匹配一个数字
\w可以匹配一个字母或数字
.可以匹配任意字符
要匹配变长的字符,在正则表达式中,用*表示任意个字符(包括0个),用+表示至少一个字符,用?表示0个或1个字符,用{n}表示n个字符,用{n,m}表示n-m个字符
\d{3}\s+\d{3,8}:可以匹配以任意个空格隔开的带区号的电话号码,
\d{3}表示匹配3个数字,例如’010’
.\s可以匹配一个空格(也包括Tab等空白符),所以\s+表示至少有一个空格,例如匹配’ ‘,’ ‘等
\d{3,8}表示3-8个数字,例如’1234567’
如果匹配’010-12345’这样的号码:\d{3}-\d{3,8}
进阶
[0-9a-zA-Z_]可以匹配一个数字、字母或者下划线
[0-9a-zA-Z_]+可以匹配至少由一个数字、字母或者下划线组成的字符串,比如’a100’,‘0_Z’,'Py3000’等等
[a-zA-Z_][0-9a-zA-Z_]*可以匹配由字母或下划线开头,后接任意个由一个数字、字母或者下划线组成的字符串,也就是Python合法的变量;
[a-zA-Z_][0-9a-zA-Z_]{0, 19}更精确地限制了变量的长度是1-20个字符(前面1个字符+后面最多19个字符)
A|B可以匹配A或B,所以(P|p)ython可以匹配’Python’或者’python’
^ 表示行的开头,^\d表示必须以数字开头
KaTeX parse error: Expected 'EOF', got '\d' at position 8: 表示行的结束,\̲d̲表示必须以数字结束
py也可以匹配’python’,但是加上^py$就变成了整行匹配,就只能匹配’py’了