Python基础知识

124 阅读12分钟

以守护进程运行python脚本,python test1.py &

1.基础知识

1.0 包package

python package需要有一个名为__init__.py的文件,不然就是普通的package,则无法import package下的模块。可以在__init__.py文件里写入要被使用的模块,这样import package时,系统会直接去__init__.py里面寻找模块或者函数。

  • 当导入一个package时,python解释器会首先在内置模块中寻找,若没有则在sys.path列表中寻找。参考

1.1 变量命名规则

python变量名只能包含以下字符:大小写字母、数字、下划线(_),名称不允许以数字开头。并且以下划线开头的名字有特殊含义。

1.2 可变对象与不可变对象

  • 可变对象:列表list、字典dict、集合set
  • 不可变对象:数值(int,float)、字符串(str)、元组(tuple)

1.3python容器

  • 列表
创建列表:a=[],a=list()
赋值:a=[],b=a  此时,a,b共享同一个对象。
复制(新表):a=[],b=a.copy(),c=list(a),d=a[:]  此时b,c,d是新的对象,不共享,和a没有任何关系。属于浅复制;浅复制只复制一层(外层)
  • 元组
创建元组:a=(),a=tuple()  a=(1,)等价于a=1,
元组元素不可更改
  • 字典
创建字典:a={},a=dict()
复制:copy()
赋值:=
  • 集合
创建集合:a=set(),a={1}
集合运算:交集(&),并集(|),差集(-)

1.4 格式化

python有两种格式化方式,习惯称为旧式和新式;这两种方式在python2和python3中都适用。

  • 使用%的旧式格式化

旧式格式化的形式为:string % data,其中string包含的式待插值序列。

 >>> '%s'%4.12
'4.12'
>>> '%f'%4.12
'4.120000'
>>> "my name is %s,age is %d "%('ss',14)
'my name is ss,age is 14 '
>>> "%10d %10f %10s"%(12,11,'dad')  #设置最小域宽度为10格字符,左侧用空格补充
'        12  11.000000        dad'
>>> "%-10d %-10f %-10s"%(12,11,'dad')  #左对齐
'12         11.000000  dad       '
>>> '%.4f %.3d'%(12.13131,11)  #数据截断,设置最大字符宽度
'12.1313 011'
  • 使用{}和format的新式格式化
>>> '{}{}'.format(12,13)
'1213'
>>> '{1}{0}'.format(12,13)  #指定插入的顺序,参数从0开始。
'1312'
>>> '{m}{n}'.format(n=12,m=13)  #命名变量参数
'1312'
>>> '{1:d} {0:f}'.format(12,13)  #使用:实现格式化类型
'13 12.000000'
>>> '{1:<4d} {0:<5f}'.format(12,13)  #左对齐<,右对齐>,居中^
'13   12.000000'
>>> '{1:^4d} {0:<5.3f}'.format(12,13) #精度和旧式一样,但也有不同
' 13  12.000'
>>> '{:^4.3}'.format(12)  #新格式中无法对整数设定精度
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>

1.5 代码注释

#单行注释1
print(123)#单行注释2

#多行注释1
#ddd
#ddd
#ddd

'''
多行注释2
ddd
'''

1.6 python2 VS python3

1.6.1 print

在python3中,print是函数,使用 print('helloworld')
            不换行输出,print("hello",end="") 
                      print("world")   输出:helloworld
在python2中,print是关键字,print "helloworld"
            不换行输出,pring "hello", 
                      print "world"   输出:hello world (以空格分割)

1.6.2 input

--------python2
>>> a=input()      #会做计算处理
12
>>> print a,type(a)
12 <type 'int'>
>>> a=raw_input()   #对输入都以字符串处理
12
>>> print a,type(a)
12 <type 'str'>
>>>
--------python3
>>> a=input()
12
>>> print(a)
12
>>> print(type(a))  #所有输入都作为字符串处理
<class 'str'>
>>>

1.6.3 整除

-----------python2
>>> print 5/2    #向下取整
2        
-----------python3
>>> print (5/2)  #默认进行浮点数除法
2.5
>>> print (5//2)  #取模
2

1.6.4 range

-------------python2
>>> print range(4)   #返回列表
[0, 1, 2, 3]
------------python3
>>> print(range(4))  #返回range对象
range(0, 4)
#循环访问py2和py3基本一样

1.6.5 字符串

python3表示字符串的两种类型:bytesstr,前者的实例包含原始的8位值(一个字节),后者实例包含unicode字符。
python2表示字符串的两种类型:unicode和str,前者包含Unicode字符,后者包含原始的8位值。
二进制转化为Unicode字符,使用decode;Unicode字符转二进制,使用encode。

1.6.6 列表推导产生的变量泄漏问题

  • python2
>>> x = 'my precious'
>>> dummy = [x for x in 'ABC']
>>> x
'C'
>>> dummy
['A', 'B', 'C']
  • python3
>>> x = 'my precious'
>>> dummy = [x for x in 'ABC']
>>> x
'my precious'
>>> dummy
['A', 'B', 'C']

python2中,在列表推导中同名变量的赋值可能会影响上下文环境;而在python3中,都有自己的局部作用域。使用生成器则不会出现这种变量泄漏问题。

1.7 项目依赖库安装

  • pip freeze > requirements.txt 导出项目的依赖。
  • pip install -r requirements.txt 在项目中一次性安装依赖

1.8 用venv 配置不同的开发环境

  • pip install virtualenv
  • 进入到项目目录,执行virtualenv --no-site-packages venv(虚拟环境到名字) no-site-packages意思是不复制系统到安装包,得到的是纯净的环境
  • 激活虚拟环境 source venv/bin/activate。进入了venv环境,pip包时只安装到该环境下,系统python环境不受任何影响(windows系统venv\Scripts\activate),激活后Python 解释器的路径就被添加进PATH 中。
  • 退出虚拟环境 deactivate,回到系统python环境

python3内置了用于创建虚拟环境的venv,可直接使用python3 -m venv .venv(Linux下)

py -3 -m venv .venv

1.9.代码风格

  • 折叠长行的首选方法是在小括号,中括号,大括号中使用Python隐式换行。长行可以在表达式外面使用小括号来变成多行。连续行使用反斜杠更好。
  • 和None比较用is
  • 缩进使用4个空格,而不要用tab
  • 不要在for和while后面用else
  • 为变量赋值时,赋值符号的左右侧应该各自写上一个空格。
  • 文件中的函数和类之间应该用两个空行隔开。
  • 在同一个类中,各方法之间应该用一个空行隔开。

1.10.新式类和旧式类

python2中既有新式类又有旧式类,并且默认为经典类,只有显示继承object才是新式类。

class a(Object):pass    #新式类写法
class a():pass        #经典类(旧式类)写法
class a:pass            #经典类写法       
类.mro()      #查看解析顺序列表,只有新式类有该属性。    
----------------------------------------------------------------------
class a():
    pass
c=a()
print dir(c)
---------------------------------out
['__doc__', '__module__']
class a(object):
    pass
c=a()
print dir(c)
----------------------------   ---out
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
*:在编写python类时,尽量使用新式类,其包含更多属性。

python3中只有新式类,默认式新式类,不必显式继承object。 新式类增加了__slots__内置属性, 可以把实例属性的种类锁定到__slots__规定的范围之中。

如果类是经典类,在多继承的情况下,会按照深度优先方式查找;如果类是新式类,在多继承的情况下,会按照广度优先方式查找;

1.11.python序列化

什么叫序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化。 json是可以在不同语言之间交换数据的,而pickle只在python之间使用。json只能序列化最基本的数据类型,而pickle可以序列化所有的数据类型,包括类,函数都可以序列化。

序列化的目的:

  • 以某种存储形式使自定义对象持久化。
  • 将对象从一个地方传递到另一个地方。
  • 使程序更具维护性。

1.11.1 Json序列化

用于字符串 和 python数据类型间进行转换

PythonJSON
dictobject
list, tuplearray
str, unicodestring
int, long, floatnumber
Truetrue
Falsefalse
Nonenull
JSONPython
objectdict
arraylist
stringunicode
number (int)int, long
number (real)float
trueTrue
falseFalse
nullNone
-----------------------------loads和dumps---------------------------
import json
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = json.dumps(dic)  #序列化:将一个字典转换成一个字符串
print(type(str_dic),str_dic)  #<class 'str'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意,json转换完的字符串类型的字典中的字符串是由""表示的

dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
#注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
print(type(dic2),dic2)  #<class 'dict'> {'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}


list_dic = [1,['a','b','c'],3,{'k1':'v1','k2':'v2'}]
str_dic = json.dumps(list_dic) #也可以处理嵌套的数据类型 
print(type(str_dic),str_dic) #<class 'str'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class 'list'> [1, ['a', 'b', 'c'], 3, {'k1': 'v1', 'k2': 'v2'}]
-------------------------load和dump----------------------------
import json
f = open('json_file','w')
dic = {'k1':'v1','k2':'v2','k3':'v3'}
json.dump(dic,f)  #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()

f = open('json_file')
dic2 = json.load(f)  #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
f.close()
print(type(dic2),dic2

1.11.2 pickle序列化

用于python特有的类型 和 python的数据类型间进行转换。pickle操作文件时,需要以二进制的方式操作。

import pickle
dic = {'k1':'v1','k2':'v2','k3':'v3'}
str_dic = pickle.dumps(dic)
print(str_dic)  #一串二进制内容

dic2 = pickle.loads(str_dic)
print(dic2)    #字典

import time
struct_time  = time.localtime(1000000000)
print(struct_time)
f = open('pickle_file','wb')
pickle.dump(struct_time,f)   #f.write(pickle.dumps(struct_time))
f.close()

f = open('pickle_file','rb')
struct_time2 = pickle.load(f)  #struct_time2=pickle.loads(f.read())
print(struct_time2.tm_year)

1.12.猴子补丁(Monkey patch)

在动态语言中,不去改变源码而对功能进行追加和变更,是在程序运行的过程中去修改。 1.追加功能 2.功能变更 3.修正程序错误
4.增加钩子,在执行某个方法的同时执行一些其他的处理,如打印日志,实现AOP等。

class XiaoMing(object):
    def favorite(self):
        print "apple"

class God(object):
    @classmethod
    def new_xiaoming_favorite(cls):
        print "banana"

    @classmethod
    def monkey_patch(cls):
        XiaoMing.favorite = cls.new_xiaoming_favorite

God.monkey_patch()

xiaoming = XiaoMing()
xiaoming.favorite()
>> banana

1.13.上下文管理器

上下文管理器是一个对象,为操作提供了额外的上下文消息,以一种优雅的方式将额外的处理封装起来。如果一个类实现了__enter____exit__方法则表明该类的实例是一个上下文管理器。上下文管理器通常和with搭配使用。实现上下文管理器的两种方式:类和装饰器

  • 类实现上下文管理器
class context(object):
    def __init__(self):
        print ('上下文管理器初始化')
    
    def __enter__(self):
        print("上下文管理器开始执行")
        return self
    
    def __exit__(self,exec_type, exec_value, exec_traceback):
        print ("上下文执行结束")

    def operator(self):
        print (1/2)
with context() as obj:
    obj.operator()
--------------------------------out
上下文管理器初始化
上下文管理器开始执行
0.5
上下文执行结束

备注 :当operator改为1/0时,程序执行后抛出异常;但是当在exit方法中返回True时(默认为False),程序不会报错,因为结果返回true就表示告诉python解释器,异常已捕获并解决,不需要往外抛了。

  • 装饰器实现上下文管理器 可以不用写一个类来实现上下文管理器,类的实现过于繁杂。python提供了一个装饰器,只需按照它的协议来实现函数内容,即可以将该函数对象变成一个上下文管理器。
from contextlib import contextmanager
from traceback import format_exc
@contextmanager
def context(a,b):
    print ("上下文管理器开始运行")  #enter方法
    f=a/b
    try:
        yield f
    except Exception:
        print ("处理异常")
    finally:
        print("finally")


with context(1,2) as f:
    print (123)
    print(f)
----------------------------------out
上下文管理器开始运行
123
0.5
finally

备注:try之前的代码会在装饰器的__enter__方法中执行;当执行到yield时,它的产出值会作为__enter__的返回值,赋值给as后的变量。当with块的代码执行完成后,上下文管理器会在yield处恢复,继续执行yield后的代码,后面的代码在装饰器的__exit__方法中被调用。 当程序发生异常时,后面的代码不会执行(包括finally代码)

上下文管理器类与@contextmanager中最大的区别在于对异常的处理。以类的方式实现的上下文管理器,在引发异常时,__exit__方法内的代码仍会正常执行;而以生成器函数实现的上下文管理器,在引发异常时,__exit__方法会将异常传递给生成器,如果生成器无法正确处理异常,则yield之后的代码不会执行。

1.14. 可散列的数据类型

如果一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是不变的,而且这个对象需要实现 hash() 方法。另外可散列对象还要有 eq() 方法,这样才能跟其他键做比较。如果两个可散列对象是相等的,那么它们的散列值一定是一样的。原子不可变数据类型(str、bytes 和数值类型)都是可散列类型。

1.15. Python 运行时

python -m xxx  #相当于import,当作模块运行,当前脚本路径不会加入到sys.path中。
python xxx.py  #直接运行,当前脚本路径会加入到sys.path中。

python -m site #显示sys.path的值内容,即python搜索模块的目录

1.16. 原地操作

许多运算符都有原地操作,如add方法对应的原地操作是iadd。python中的运算符和operator模块中的函数是对应的

  • 不可变类型的目标

​ 对于不可变的目标如字符串、数字、元组等,当调用一个原地方法是,分两步执行,运算和赋值。当调用原地函数时,只执行运算嘛,不执行赋值操作。如下:

>>> import operator
>>> a="hello"
>>> operator.iadd(a, ' world')#若要更新变量,则需要执行赋值操作,即a=operator.iadd(a, ' world')
'hello world'
>>> a
'hello'
  • 可变类型的目标

    对于可变的目标,如列表、字典,原地方法将执行更新,因此不需要后续赋值操作。如下:

>>> import operator
>>> s = ['h', 'e', 'l', 'l', 'o']
>>> iadd(s, [' ', 'w', 'o', 'r', 'l', 'd'])
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
>>> s
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']

2.错误解决方法

2.1 ImportError错误

  1. 普通的packagepython packae
     错误分析</strong>:from package import xxx 时,package无法被识别为python package
     解决办法:在package下新建__init__.py即可,此时package被视为python package
  1. 循环导入
    from a  inport b  # module a
    from b import a   # module b

解决办法:

  1. 在模块的最后使用import语句。
  2. 在函数内导入模块(即使用到该模块内容的函数内部)