感谢传至辛苦奉献的老师
目录
0. dir
函数查看对象的方法/属性
1. OOP基本语法
面向对象三大特性
- 封装 根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
- 继承 实现代码的重用,相同的代码不需要重复的编写
- 多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
def Person(object):
pass
- 使用 print 输出 对象变量,默认情况下,是能够输出这个变量 引用的对象 是 由哪一个类创建的对象,以及 在内存中的地址(十六进制表示)
1. self
参数
- 在 类的外部,通过 变量名. 访问对象的 属性和方法
- 在 类封装的方法中,通过 self. 访问对象的 属性和方法
由 哪一个对象 调用的方法,方法内的
self
就是 哪一个对象的引用
2. __init__
当使用 类名() 创建对象时,会 自动 执行以下操作:
- 为对象在内存中 分配空间 —— 创建对象
- 为对象的属性 设置初始值 —— 初始化方法(init)
3. __del__
- 当使用 类名() 创建对象时,为对象 分配完空间后,自动 调用 init 方法
- 当一个 对象被从内存中销毁 前,会 自动 调用 del 方法
class Cat(object):
def __init__(self, new_name):
self.name = new_name
print("%s 来了" % self.name)
def __del__(self):
print("%s 去了" % self.name)
# tom 是一个全局变量
tom = Cat("Tom")
print(tom.name)
# del 关键字可以删除一个对象
del tom
print("-" * 50)
4. __str__
2. 面向对象封装
1. 身份运算符is
is
用于判断 两个变量 引用对象是否为同一个==
用于判断 引用变量的值 是否相等
在 Python 中针对 None 比较时,建议使用 is 判断
2. 私有属性和方法
- 在属性/方法前加上一个
_
- 在属性/方法前加上两个
_
, 这种方式实际是对 名称 做了一些特殊处理,使得外界无法访问到处理方式:在 名称 前面加上 _类名 =>_类名__名称
3. 继承
class 类名(父类名):
pass
1. 重写父类方法
# 方法1——推荐使用
super().父类方法()
# 方法2
父类名.方法(self)
在Python中, super
是一个特殊的类,super()
就是创建这个类的对象,最常使用的场景就是重写父类方法的时候。在python2的早期没有super
,所以只能使用上面的方法2来调用。
推荐使用方法一,因为如果父类名更改了,得重新修改子类代码
2. 父类的私有属性/方法
- 子类对象 不能 在自己的方法内部,直接 访问 父类的 私有属性 或 私有方法
- 子类对象 可以通过 父类 的 公有方法 间接 访问到 私有属性 或 私有方法
- 私有属性、方法 是对象的隐私,不对外公开,外界 以及 子类 都不能直接访问
3. 多继承
class C(A, B):
pass
在多继承的情况下,调用super().__init__()
,只调用一个父类的初始化方法
class A(object):
def __init__(self):
print("A")
class B(object):
def __init__(self):
print("B")
class C(A, B):
def __init__(self):
super().__init__()
print("C")
c = C()
# A
# C
提示:开发时,应该尽量避免这种容易产生混淆的情况! —— 如果 父类之间 存在 同名的属性或者方法,应该 尽量避免 使用多继承
a. Python 对象的 __mro__
Python 中针对 类 提供了一个 内置属性 mro 可以查看 方法 搜索顺序, MRO 是 method resolution order,主要用于 在多继承时判断 方法、属性 的调用 路径
print(类名.__mro__)
# (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
4. 新式类和经典类
object
类 是 Python 为所有对象提供的 基类,提供有一些内置的属性和方法,可以使用 dir 函数查看。
- 新式类:以 object 为基类的类,推荐使用
- 经典类:不以 object 为基类的类,不推荐使用
class A(object):
"""新式类"""
pass
class B:
"""经典类,python2中才有"""
pass
python2和python3中的类区别:
- 在 Python 3.x 中定义类时,如果没有指定父类,会 默认使用 object 作为该类的 基类 —— Python 3.x 中定义的类都是 新式类
- 在 Python 2.x 中定义类时,如果没有指定父类,则不会以 object 作为 基类
python2中如果不指定继承自
object
类,则是经典类,它里面没有关于object
类的__XXX__
方法
新式类 和 经典类 在多继承时 —— 会影响到方法的搜索顺序
class FatherA(object):
def demo(self):
print("A father")
class A(FatherA):
pass
class FatherB(object):
def demo(self):
print("B father")
class B(FatherB):
pass
class C(A, B):
pass
c = C()
c.demo() # A father
# 搜索顺序先搜索A,找不到再去搜索A的祖先,而不是直接搜索B
4. 多态
面向对象三大特性
-
封装 :根据 职责 将 属性 和 方法 封装 到一个抽象的 类 中
- 定义类的准则 继承: 实现代码的重用,相同的代码不需要重复的编写
- 设计类的技巧
- 子类针对自己特有的需求,编写特定的代码 多态 不同的 子类对象 调用相同的 父类方法,产生不同的执行结果
-
- 多态 可以 增加代码的灵活度
-
- 以 继承 和 重写父类方法 为前提
-
- 是调用方法的技巧,不会影响到类的内部设计
5. 类属性和类方法
通过类名()
创建对象的时候,有2个步骤:
- 在内存中为对象 分配空间——使用
__new__
- 调用初始化方法
__init__
为 对象初始化
多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用(self
)传递到方法内部
5.1 类是特殊的对象
在Python中,类是一个特殊的对象:class AAA:
定义的类属于 类对象。在程序运行时,类 同样 会被加载到内存, 类对象 在内存中 只有一份。
除了封装 实例 的 属性 和 方法外,类对象 还可以拥有自己的 属性 和 方法
5.2 属性的获取机制
在Python中,对类属性存在一种向上查找机制:
获取属性值:
类名.类属性
对象.类属性
(不推荐)
注意使用
对象.类属性
赋值时的情况
5.3 类方法
在 类方法 内部可以直接访问类属性 或者调用其他的 类方法
@classmethod
def 类方法名(cls):
pass
类名.类方法()
python会自动将类本身作为第一个参数,传递给cls
5.4 静态方法
在开发时,如果需要在 类 中封装一个方法,这个方法:
- 既 不需要 访问 实例属性 或者调用 实例方法
- 也 不需要 访问 类属性 或者调用 类方法
@staticmethod
def 静态方法名():
pass
类名.方法名()
6. 单例
6.1 使用 __new__
方法
__new__
方法作用:
- 内存中为对象分配空间
- 返回对象引用
a. 对象创建过程
- 对象调用
__new__
这个静态方法,为对象分配空间,将这个引用返回 - 拿到这个对象引用,将其传递给
__init__
方法进行初始化 - 返回这个对象
b. 重写__new__
方法
调用语法为:
def __new__(cls, *args, **kwargs):
return super().__new__(cls) # 该方法为静态方法,要手动传递 cls
__new__
不返回对象的结果
def Person(object):
def __new__(cls, *args, **kwargs):
print("创建")
def __init__(self):
print("初始化")
p = Person() # 打印 “创建”
print(p) # 打印 None
# 因为 __new__没有返回对象,所以没有调用__init__
__new__
返回对象的结果
使用父类(object)自身的__new__
方法来分配空间
def Person(object):
def __new__(cls, *args, **kwargs):
print("创建")
# 分配空间
instance = super().__new__(cls) # 因为__new__是静态对象,所以cls要主动传递
# 返回对象
return instance
def __init__(self):
print("初始化")
p = Person() # 打印 “创建” 、初始化
print(p) # 打印地址
6.2 设计单例
class Person(object):
singleton = None
def __new__(cls, *args, **kwargs):
if cls.singleton is None:
cls.singleton = super().__new__(cls)
return singleton
6.3 只初始化一次
虽然单例之后,对象的引用都是一个,但是这个引用每次都会在之后传递给__init__
方法,这里就是使得__init__
方法也只执行一次。
方法:使用flag
class Person(object):
singleton = None
init_flag = False
def __new__(cls, *args, **kwargs):
if cls.singleton is None:
cls.singleton = super().__new__(cls)
return singleton
def __init__(self):
if Person.init_flag: # 已初始化,直接返回
return
print("初始化")
Person.init_flag = True
注意,在Python中访问类属性的时候,记住 使用
类名/cls.属性名
,不要漏了,这样就变成了局部变量
7. 异常
程序开发时,很难将 所有的特殊情况 都处理的面面俱到,通过 异常捕获 可以针对突发事件做集中的处理,从而保证程序的 稳定性和健壮性
7.1 完整语法
try:
# 尝试执行的代码
pass
except 错误类型1:
# 针对错误类型1,对应的代码处理
pass
except 错误类型2:
# 针对错误类型2,对应的代码处理
pass
except (错误类型3, 错误类型4):
# 针对错误类型3 和 4,对应的代码处理
pass
except Exception as result:
# 打印错误信息
print(result)
else:
# 没有异常才会执行的代码
pass
finally:
# 无论是否有异常,都会执行的代码
print("无论是否有异常,都会执行的代码")
7.2 异常的传递
异常的传递 —— 当 函数/方法 执行 出现异常,会 将异常传递 给 函数/方法 的 调用一方(上抛),直到主程序
7.3 抛出异常raise
可以根据特定业务逻辑抛出自己的异常
- 创建
Exception
对象 - 使用
raise
抛出对象
raise Exception("这有问题")
# 捕获并且打印信息
except Exception as result:
print(result): # 这有问题
8. 模块和包
- 每一个python文件就是一个模块,模块中的全局变量,函数,类可以被外部来使用。
- 模块名也应该符合标识符命名规则
8.1 模块导入
1. import 导入
import 模块名1, 模块名2 # 可以,但是不符合标准
import 模块名1
import 模块名2
2. 模块重命名
模块别名应该使用 大驼峰命名法
import 模块名1 as 模块别名
3. from…import 导入
如果希望 从某一个模块 中,导入 部分 工具,就可以使用 from … import 的方式
from 模块名1 import 工具名1,工具名2
注意:使用from
导入的,被导入的对象直接在主模块中存在; 而使用import
引入的,成员还是在对应的模块中
4. 使用as
来解决冲突
注意: 这种方式导入,如果存在同名成员,则后导入的会覆盖先导入的:
from module1 import test
from module2 import test
test() # 执行模块2的
解决冲突:
from module1 import test
from module2 import test as test2
test() # 执行模块1
test2() #执行模块2
5. from … import *
8.2 模块搜索顺序——sys.path, __file__
会先在当前目录下查看,所以自己写的模块应该避免跟系统模块重名。
sys.path
会打印模块的搜索路径- 很多模块有一个
__file__
,被引入后可以查看这个,它表示该模块的文件位置
8.3 模块的__name__
原则 —— 每一个文件都应该是可以被导入的
在导入文件时,文件中 所有没有任何缩进的代码 都会被执行一遍!
因为模块内部代码写的时候,可能还附带有测试代码,所以为了能够区分测试和使用,模块的__name__
就派上用场了:
__name__
本身是字符串- 如果 是被其他文件导入的,
__name__
就是 模块名 - 如果 是当前执行的程序
__name__
是__main__
python文件结构:
# 导入模块
# 定义全局变量
# 定义类
# 定义函数
# 在代码的最下方
def main():
# ...
pass
# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
main()
8.4 包(package)
- 包 是一个 包含多个模块 的 特殊目录
- 目录下有一个 特殊的文件
__init__.py
- 包名的 命名方式 和变量名一致,小写字母 + _
可以从包导入模块:
mypack
-----module1
-----module2
1. from… import
from mypack import module1, module2
module1.func()
module2.func()
2. import
要现在__init__.py
中添加能被外界访问的模块:
# __init__.py中,添加从 当前目录 导入 模块列表
from . import module1 # .代表当前文件夹
from . import module2
# 外部文件中:
import mypack
mypack.module1.func()
mypack.module2.func()
8.5 发布自己的包
1. 制作压缩包
-
1.制作发布压缩包
-
在和包相同文件夹下,创建
setup.py
文件,里面输入这些代码:
from distutils.core import setup
setup(name="名称", # 包名
version="1.0", # 版本
description="描述", # 描述信息
long_description="长描述信息", # 完整描述信息
author="作者", # 作者
author_email="邮箱", # 作者邮箱
url="网址", # 主页
py_modules=["包名.模块名1",
"包名.模块名2"])
-
2.构建模块
$ python3 setup.py build
-
3.生成发布压缩包
$ python3 setup.py sdist
# 就会生成 .tar.gz文件,可以拷贝给别人使用
要制作哪个版本的模块,就使用哪个版本的解释器执行!
2. 安装模块
$ tar -zxvf hm_message-1.0.tar.gz
# 进入这个压缩后的文件夹,运行:
$ sudo python3 setup.py install
# 模块就会被安装到 dist-packages
3. 删除模块
直接在dist-packages
中删除对应的文件夹就可以了
4. 使用pip安装第三方模块
# 将模块安装到 Python 2.x 环境
$ sudo pip install pygame
$ sudo pip uninstall pygame
# 将模块安装到 Python 3.x 环境
$ sudo pip3 install pygame
$ sudo pip3 uninstall pygame
# Linux 下安装,是通过不同的名称来区分的
$ sudo apt install ipython
$ sudo apt install ipython3
9. 文件
9.1 访问方式
访问方式 | 说明 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛 |
w | 以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a | 以追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
r+ | 以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常 |
w+ | 以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a+ | 以读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
# 打开文件
file = open("README")
while True:
# 读取一行内容
text = file.readline()
# 判断是否读到内容
if not text:
break
# 每读取一行的末尾已经有了一个 `\n`
print(text, end="")
# 关闭文件
file.close()
9.2 OS模块的操作
文件操作
序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | rename | 重命名文件 | os.rename(源文件名, 目标文件名) |
02 | remove | 删除文件 | os.remove(文件名) |
目录操作
序号 | 方法名 | 说明 | 示例 |
---|---|---|---|
01 | listdir | 目录列表 | os.listdir(目录名) |
02 | mkdir | 创建目录 | os.mkdir(目录名) |
03 | rmdir | 删除目录 | os.rmdir(目录名) |
04 | getcwd | 获取当前目录 | os.getcwd() |
05 | chdir | 修改工作目录 | os.chdir(目标目录) |
06 | path.isdir | 判断是否是文件 | os.path.isdir(文件路径) |
10. 编码
Python2中使用中文:
在文件开头加上下面这个,解释器就会以 utf-8来处理文件
# *-* coding:utf8 *-*
# 或者
# coding=utf8
Python2中使用中文字符串遍历:
在 Python 2.x 中,即使指定了文件使用 UTF-8 的编码格式,但是在遍历字符串时,仍然会 以字节为单位遍历 字符串。要能够 正确的遍历字符串,在定义字符串时,需要 在字符串的引号前,增加一个小写字母 u
,告诉解释器这是一个 unicode 字符串(使用 UTF-8 编码格式的字符串)
# *-* coding:utf8 *-*
# 在字符串前,增加一个 `u` 表示这个字符串是一个 utf8 字符串
hello_str = u"你好世界"
print(hello_str)
for c in hello_str:
print(c)
11. eval
将字符串当作代码操作,返回结果:
# 基本的数学计算
In [1]: eval("1 + 1")
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': 'xiaoming', 'age': 18}"))
Out[4]: dict
小心攻击:
__import__('os').system('ls')
# 这条语句相当于
import os
os.system("终端命令")