全局变量
- 在函数内部无法修改全局变量的值
- 即使修改,解释器也会认为是在函数内部定义了一个与全局变量名称一样的局部变量
- 如果在函数内部需要修改全局变量,需要使用global关键字
代码结构的定义规范
- shebang
- import 模块
- 全局变量
- 函数定义
- 执行代码
注意:定义全局变量的时候应该在变量名前面加
g_
或者gl_
的前缀
函数返回值
- 返回多个值返回元组即可
- 使用多个变量接受返回的元组中的数据,格式
变量一,变量二... = 函数
- 使用多变量接受结果的时候,变量的个数应该和元组中元素的个数保持一致,否则会报错
Python交换数字的特殊方式
a = 10
b = 20
# 方式一
a = a+b
b = a-b
a = a-b
print("a = %d,b = %d" % (a, b))
# 方式二
a, b = (b, a)
print("a = %d,b = %d" % (a, b))
函数的参数
- 无论是可变的还是不可变的参数,针对参数使用
赋值语句
,都不会影响到调用函数时传递的实参变量,影响范围只在函数内部 - 如果传递的参数是
可变类型
,在函数内部,使用方法
修改了数据的内容,这样才会影响到外部数据 - 列表变量使用
+=
符号本质是在调用列表的extend
方法,所以仍然会修改函数外部的实参
缺省参数
定义
- 定义函数时,可以给某个参数制定一个默认值,具有默认值的参数就叫缺省参数
- 函数缺省参数的作用就是将常见的值设置为参数的缺省值,从而简化函数的调用
使用场景
- 在参数后面使用赋值语句,可以指定参数的缺省值,在指定缺省值的时候需要使用最常见的值作为默认值
- 如果一个参数的值不能确定,这不应该设置默认值
注意点
- 缺省参数的定义位置必须在参数列表末尾
- 调用带有多个缺省参数的函数时,需要指定参数名,这样解释器才能知道参数的对应关系
多值参数
定义支持多值参数的函数
* 有时可能需要一个函数能够处理的参数个数是不确定的,这个时候就可以使用多值参数
* python
中有两种多值参数:
* 参数名前面加一个*
可以接受元组
* 参数名前面加两个*
可以接受字典
* 一般给多值参数命名时,习惯使用以下两个名字
* *args
– 存放元组参数,前面有一个*
* **kwargs
– 存放 字典参数,前面有两个*
* args
是arguments
的缩写,有变量的含义
* kw
是keyword
的缩写,kwargs
可以记忆为键值对参数
多值参数常用于框架当中!
元组的字典的拆包
再调用带有多值参数的函数时,如果希望:
- 将一个元组变量,直接传递给
args
- 将一个字典变量,直接传递给
kwargs
- 将一个元组变量,直接传递给
就可以使用拆包,简化参数的传递,拆包的方式是:
- 在元组变量前,增加一个
*
- 在字典变量前,增加两个
*
- 在元组变量前,增加一个
使用示例:
def demo(*args, **kwargs): print(args) print(kwargs) gl_nums = (1, 2, 3) gl_dict = {"name": "Tim", "age": 18} demo(gl_nums, gl_dict)
很显然这不是我们所期望看到的结果,因为解释器把字典当做元组的一个元素了,此时便需要拆包
def demo(*args, **kwargs): print(args) print(kwargs) gl_nums = (1, 2, 3) gl_dict = {"name": "Tim", "age": 18} # demo(gl_nums, gl_dict) # 拆包的做法 demo(*gl_nums,**gl_dict) # 不拆包的做法 demo(1, 2, 3, name="Tim", age=18)
函数递归
- 函数调用自身称为递归,函数内部的代码是相同的,只是针对参数不同处理的结果也不同
- 当参数满足一个条件时,函数不再执行,这是递归的出口
面向对象
dir内置函数
使用内置函数dir
传入标识符/数据,可以查看对象内部的所有属性和方法
提示__方法名__
格式的方法是python提供的内置方法/属性
序号 | 方法名 | 类型 | 作用 |
---|---|---|---|
01 | __new__ | 方法 | 创建对象时,会被自动调用 |
02 | __init__ | 方法 | 对象被初始化时,会被自动调用 |
03 | __del__ | 方法 | 对象被从内存中销毁前,会被自动调用 |
04 | __str__ | 方法 | 返回对象的描述信息,print函数输出使用 |
所以在Python中方法也是对象!!
class MyClass:
def fun(self):
pass
# 创建对象
obj = MyClass()
# print函数会直接打印出对象所属类以及地址
print(obj)
# 使用id函数获取 对象的内存地址
address = id(obj)
print(address)
# 以16进制打印地址
print("%x" % address)
self参数
在python中要给对象设置属性,非常容易,但是不推荐使用,因为对象的属性应该封装在类的内部,设置方法是对象.属性 = 值
,这种方式虽然简单但是不推荐使用!
哪一个对象调用的方法,self就是哪一个对象的引用
class Student:
def study(self):
print("%s开始学习" % self.name)
self.eat()
def eat(self):
pass
s1 = Student()
s1.name = "Tim"
s1.study()
self参数也可以访问到类中属性和方法,在开发中不推荐在类的外部给类增加属性,如果在运行时没找到属性,程序就会报错。所有属性都应该封装在类的内部
__init__方法
__init__
方法相当于Java中的构造方法!
当使用
类名()
创建对象是,会自动分配空间并执行__init__
方法,__init__
是对象的内置方法,专门用来定义一个类具有哪些属性的方法,在__init__
方法内部使用self.属性名= 初始值
就可以定义属性!对
__init__
方法加上参数,就可以在__init__
内部使用self.属性 = 形参
接收外部传递的参数在创建对象时,使用
类名(属性1, 属性2...)
的形式即可完成对象的构造
__del__方法
__del__
方法就相当于C++中的析构函数!
当使用类名()
创建对象时,自动调用__init__
方法,当对象从内存中销毁之前,会自动调用__del__
方法
应用场景
__init__
改造初始化方法,可以让创建对象更加灵活__del__
如果希望在对象被销毁前做些事情,考虑使用__del__
方法
生命周期
__init__
————–>__del__
__str__方法
相当于Java的toString方法
直接使用print打印对象获得的结果是该对象的所属类和在内存中的地址,如果希望自定义内容,__str__
方法是不二之选!
身份运算符
is
或is not
,注意is
与==
的区别:
is
用来判断两个不舒服引用的对象是否是同一个,相当于Java的==
==
用来判断引用变量的值是否相等,相当于Java的equest
方法
私有属性和私有方法
定义方式
在定义属性或者方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法
伪私有属性和私有方法
Python并没有真正意义上的私有
在给属性和方法命名时,实际是对名称做了一些特殊的处理,使的外界无法访问
处理方式:在名称前面加上_类名
=>_类名__名称
继承
格式:class 类名(父类名)
子类继承父类的所有属性和方法
方法重写
重写父类方法有两种情况
- 覆盖父类中的方法
- 对父类中的方法进行扩展
- 重写之后只会调用子类中重写的方法,而不会调用父类中封装的方法
关于super
- 在python中super是一个特殊的类
super()
就是使用spuer
类创建出来的对象- 最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现
调用父类方法的另一种方式,在Python2.x时,如果需要调用父类的方法,还可以使用
父类名.方法(self)
,这种方式目前在Python3.x还支持,但是不推荐使用
在开发中父类名
和super()
两种方式不要混用,如果使用当前子类名调用方法会形成递归调用,出现死循环
父类的私有属性和私有方法
- 子类对象不能在自己的方法内部直接访问父类的私有属性或者私有方法
- 子类对象可以通过父类的公有方法简介访问到私有属性或者私有方法,例如
Java的get和set
- 私有属性、方法是对象的隐私,不对外公开,外界及其子类都不能访问
多继承
子类可以有多个父类,并且具有所有父类的属性和方法
格式class 子类名(父类名1,父类名2... )
如果不同的父类中存在同名的方法,子类在调用父类中的方法时会不明确调用哪一个方法,所以开发时应该尽且避免这种容易产生混淆的情况!如果父类之间存在同名的属性或方法.应该尽量避免使用多继承!
Python中的MRO – 方法搜索顺序
- Python中针对类提供了一个内置属性
__mor__
可以查看方法搜索顺序 - MRO的全称是
method reslution order
,主要是用于在多继承的时候判断方法、属性的调用路径
class A:
pass
class B:
pass
class C(A, B):
pass
print(C.mro())
输出:[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
- 在搜索方法时,时按照
__mro__
的输出结果从左至右的顺序查找的 - 如果在当前类中找到方法,就直接执行,不再搜索
- 如果没有找到,就查找下一个类中是否有对应的方法,如果找到,就直接执行,不再搜索
- 如果找到最后一个类买还是没有找到方法,程序报错
新式类与旧式(经典)类
object
是python为所有对象提供的基类,提供有一些内置的属性和方法,使用dir
函数查看
新式类:以object作为基类的类,推荐使用
经典类:不以object作为基类的类,不推荐使用
Python3.x中所有的类都是新式类
Python2.x中如果没有指定父类,则不会以object作为父类
新式类和旧式类在多继承时会影响到方法的搜索顺序,建议统一使用新式类
多态
不同的子类对象,调用的相同的父类方法,产生不同的执行结果
增加代码的灵活度
以继承和重写父类方法为前提
是调用方法的技巧,不会影响到类的内部设计
每一个对象都有自己的独立的内存空间,保存各自不同的属性
每个对象的方法在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部
Python中一切皆对象
class AAA:
定义的类属于类对象
obj = AAA()
属于实例对象
在程序运行时,类同样会被加载到内存,在Python中类是一个特殊的对象
在程序运行时,类对象在内存中只有一份
除了封装实例的属性和方法外,类对象还可以拥有自己的属性和方法
- 类属性
- 类方法
通过
类名.
的方式可以访问类的属性或者调用类的方法
类属性和实例属性
- 类似于Java的static成员变量
- 类属性就是给类对象中定义的属性
- 通常用来记录与这个类相关的特征
- 类属性不会用于记录对象的特征
示例需求
- 定义一个工具类
- 每件工具都有自己的name
- 需求 – 直到使用这个类,创建了多少个工具对象?
class Tool:
# 定义类属性
count = 0
def __init__(self, name):
self.name = name
# 让类属性的值加一
Tool.count += 1
t1 = Tool("AAA")
t2 = Tool("BBB")
t3 = Tool("CCC")
t4 = Tool("DDD")
print(t1.count) # 总创建了4个对象
属性的获取机制
在Python中属性的获取存在一个向上查找机制,首先在对象内部查找对象属性,没有的话就会向上查找类属性
所以访问类属性有两种方式:
类名.类属性
对象.类属性
(不推荐使用)
注意如果使用对象.类属性 = 值
赋值语句,只会对给对象添加一个属性,而不会影响到类属性的值
类方法与静态方法
类方法与类属性一致,类方法是针对这个类而定义的方法,而不是针对对象定义的方法
类方法
语法如下:
class 类名:
@classmethod
def 类方法名(cls):
pass
类方法需要使用
@classmethod
来标识,告诉解释器这是一个类方法类方法的第一个参数应该是
cls
- 由哪一个类调用的方法,方法内的
cls
就是那一个类引用 - 这个参数与实例方法的
self
比较相似 - 使用其他名称也可以,不一定非的是cls,不过习惯使用
cls
- 由哪一个类调用的方法,方法内的
通过
类名.
的方式调用类方法时不需要传递cls
参数在方法内部
可以通过
cls.
访问类属性可以通过
cls.
调用类方法
静态方法
如果一个方法需要访问实例属性,那么封装为实例方法,如果需要访问类属性,使用类名.
访问类属性
如果一个方法需要访问类属性,那么封装为类方法
如果一个方法既不需要访问实例属性,也不需要访问类属性,那么封装为静态方法
语法如下:
@staticmethod
def 静态方法名称():
pass
- 静态方法需要用修饰器
@staticmethod
来标识,告诉解释器这是静态方法 - 通过
类名.
调用静态方法
单例模式
内存中只有一个该类的实例
- 定义一个类属性,初始值为
None
,用来记录单例对象的引用 - 重写
__new__
方法 - 如果类属性
is None
,调用父类方法并分配空间,并在类属性中记录结果 - 返回类属性中记录的结果
__new__方法
使用类名()创建对象时,python的解释器会自动调用
__new__
方法为对象分配空间__new__
是一个由object
基类提供的内置静态方法,作用- 在内存中为对象分配空间
- 返回对象的引用
python的解释器获取引用后,将引用作为第一个参数,传递给
__init__
方法重写
__new__
方法的代码非常固定!重写
__new__
方法一定要return super().__new__(cls)
否则Python的解释器得不到分配空间的对象引用,就不会调用对象的初始化方法
注意:
__new__
是一个静态方法,在调用时需要主动传递cls
参数
class MusicPlayer(object):
# 记录第一个被创建对象的引用
instance = None
def __new__(cls, *args, **kwargs):
# 判断类属性是否为空对象
if cls.instance is None:
# 调用父类方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 返回类属性保存的对象引用
return cls.instance
m1 = MusicPlayer()
m2 = MusicPlayer()
print(m1)
print(m2)
< __main__.MusicPlayer object at 0x000001F9A381F8D0>
<__main__.MusicPlayer object at 0x000001F9A381F8D0>
上面的做法虽然可以完成单例,但是初始化方法还是执行了两次,稍加修改让init方法也执行一次
class MusicPlayer(object):
# 记录第一个被创建对象的引用
instance = None
# 初始化方法调用标记
init_flag = False
def __new__(cls, *args, **kwargs):
# 判断类属性是否为空对象
if cls.instance is None:
# 调用父类方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 返回类属性保存的对象引用
return cls.instance
def __init__(self):
if MusicPlayer.init_flag is False:
super()
MusicPlayer.init_flag = True
print("init()...")
m1 = MusicPlayer()
m2 = MusicPlayer()
print(m1)
print(m2)
异常
捕获异常
- 简单基本格式
try:
尝试执行的代码
except:
出现错误的处理
- 错误类型捕获,针对不同类型的异常,做出不同的处理
try:
pass
expect 错误类型1:
# 针对错误类型1的处理
pass
expect 错误类型2:
# 针对错误类型2的处理
pass
expect Exception as result:
print("未知错误%s" % result)
捕获异常的完整语法
try:
# 尝试执行的代码
pass
expect 错误类型1:
# 针对错误类型1的处理
pass
expect 错误类型2:
# 针对错误类型2的处理
pass
except(错误类型3,错误类型4):
pass
except Exception as result:
# 打印错误信息
print(result)
else:
# 没有异常才会执行的代码
pass
finally:
# 必须要执行的代码
异常的传递
如果在某个函数中发生异常却未处理,异常会传递给调用方,会层层向上传递直到有遇到处理异常的代码为止,如果都没有处理此异常将会传递至主程序!
手动抛出异常
Python提供了一个Exception
异常类,如果希望抛出异常先创建一个Exception
的对象,然后使用raise
关键字抛出异常对象即可!
模块
import 模块名1,模块名2
不推荐使用,每个导入应该独占一行import 模块名 as 名称简写
给模块起别名from 模块名 import 工具名
从某一个模块中导入部分工具,这种方式导入的话不需要通过模块名.
的方式来访问的,导入之后可以直接使用模块提供的工具—–全局变量、函数、类
注意如果两个模块存在同名的函数,那么后导入的模块的函数会覆盖先导入的函数!
from 模块名 import *
这样可以一次性把模块中的所有工具全部导入,不推荐使用
模块的搜索顺序
搜索当前目录指定模块名的文件,如果有就直接导入,没有的话再搜索系统目录
注意:在开发时给文件起名不要和系统模块文件重名!
Python中每一个模块都有一个内置属性__file__
可以查看模块的完整路径
原则–每一个文件都应该是可以被导入的
在导入文件时,文件中所有没有任何缩进的代码都会被执行一遍
应用场景:模块的开发者通常会在模块下方增加一些测试代码,仅仅在模块内补使用,不会被到导入到其他文件中
__name__属性
__name__
属性可以做到,测试模块的代码只在测试情况下运行,而被导入时不会被执行__name__
时python的一个内置属性,记录着一个字符串如果是被其他文件导入的,
__name__
就是模块名如果是当前执行的程序,
__name__
是__main__
if __name__ == "__main__": # 测试代码 pass
包(Package)
包下必须有一个特殊的文件__init__.py
,使用import 包名
可以导入包下所有的模块
__init__.py
- 要在外界使用包中的模块,需要在
__init__.py
中指定对外提供的模块列表
# 从当前目录导入模块列表
from . import 模块名
发布压缩包的制作
- 创建
setup.py
文件,关于字典参数的详细信息,http://docs.python.org/2/distutils/apiref.html - 构建模块
python2 setup.py build
- 生成发布压缩包
puthon3 setup.py sdist
安装/卸载模块
- 先解压
tar -zxvf 文件名
- 再安装
sudo python3 setup.py install
cd /usr/local/lib/python3.5/dist-packages
再sudo rm -f XXX
即可卸载
pip安装第三方模块
pip是一个通用的python包管理工具,提供了对Python包的查找、下载、安装、卸载等功能
例如安装pygame这个模块sudo pip install pygame
但是这样只能将模块安装到Python2.x环境,如果需要安装到Python3.x的环境,需要使用sudo pip3 install pygame
文件
在Python中操作文件需要记住1个函数3个方法
函数/方法 | 说明 |
---|---|
open(函数) | 打开文件,并且返回文件操作对象 |
read | 将文件读取到内存 |
write | 将指定内容写入文件 |
close | 关闭文件 |
open函数默认以只读方式打开文件,并且返回文件对象
访问方式 | 说明 |
---|---|
r | 以只读方式打开,这是默认模式,如果文件不存在则抛出异常 |
w | 以只写方式打开文件,如果文件存在会被覆盖,如果文件不存在则创建新文件 |
a | r+以追加方式打开文件,如果文件存在,文件指针将会放到末尾,若不存在创建新文件 |
r+ | 以读写方式打开文件,文件的指针将会放在文件的开头,如果文件不存在,抛出异常 |
w+ | 以读写方式打开文件,如果文件存在,文件指针将会放在文件的开头,如果不存在创建新文件 |
a+ | 以读写方式打开文件,如果文件已经存在,文件指针将会放在文件的结尾,若不存在就创建新文件写入 |
file = open("a.txt", "r")
while True:
text = file.readline()
if not text:
break
print(text, end="")
文件/目录的常用管理操作
创建、重命名、删除、更改路径、查看目录内容…
Python 中如果希望通过程序实现上述功能需要导入os
模块
文件操作
方法名 | 说明 | 示例 |
---|---|---|
rename | 重命名文件 | os.rename(源文件名,目标文件名) |
remove | 删除文件 | os.remove(文件名) |
目录操作
方法名 | 说明 | 示例 |
---|---|---|
listdir | 目录列表 | os.listdir(目录名) |
mkdir | 创建目录 | os.mkdir(目录名) |
rmdir | 删除目录 | os.rmdir(目录名) |
getcwd | 获取当前目录 | os.getcwd() |
chdir | 修改工作目录 | os.chdir(目标目录) |
path.isdir | 判断是否是文件 | os.psth.isdir(文件路径) |
注意:文件或者目录都支持相对路径和绝对路径
文本文件的编码格式
- 文本文件存储的内容是基于字符编码的文件,常见的编码有
ASCII
编码,UNICODE
编码 - Python2.x默认使用
ASCII
编码 - Python3.x默认使用
UTF-8
编码
在Python2.x文件的第一行增加以下注释,解释器会以UTF-8
编码来处理文件,这也是官方推荐的方式
# *-* coding:utf8 *-*
Python2.x中使用中文字符串需要在字符串前面加小写u,例如str = u"中文"
即可!
eval函数
- 基本的数学计算,与JavaScript中的eval函数一致
In [1]: eval("5-3")
Out[1]: 2
In [2]: eval("'*' * 10")
Out[2]: '**********'
In [3]: type(eval("[1, 2, 3, 4, 5]"))
Out[3]: list
In [4]: type(eval("{'name':'Tim', 'age':20}"))
Out[4]: dict
注意在开发时不要使用eval函数转换input的结果!
一旦用户输入__import__('os').system('终端命令')
这样的是非常可怕的!