Object Oriented Programming
简写 OOP
面向对象是更大的封装,根据职责在一个对象中封装多个方法
游戏中,每个角色的技能职责不同,就很像这一个个对象。它们的技能就是各种方法。
- 特征被称为属性(变量)
- 行为 被称为方法(函数)
- 不同的对象之间属性可能会各不相同
- 类名:大驼峰命名法
- 属性:事物的特征
- 方法:事物的行为
需求中没有涉及的属性或者方法在设计类时,不需要考虑
python
中,一切皆是对象__方法名__
格式的方法是python
提供的内置方法/属性方法名 | 类型 | 作用 |
---|---|---|
__new__ | 方法 | 创建对象时,会被 自动 调用 |
__init__ | 方法 | 对象被初始化时,会被 自动 调用 |
__del__ | 方法 | 对象被从内存中销毁前,会被 自动 调用 |
__str__ | 方法 | 返回对象的描述信息,print 函数输出使用 |
__doc__ | 方法 | 对象中的文档说明 |
Class 类名:
变量1 = 值1
变量2 = 值2
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass
对象名(是一个变量) = 类名()
print(one_class) # <__main__.OneClass object at 0x01BAC2D0>
上面的print函数打印了一个对象,其显示的结果是:该对象所属的类 at 内存中的地址(十六进制表示)
实例名.新属性名 = 属性值
- 在日常开发中,对象应该包含哪些属性,应该封装在类的内部
- self 本来就是一个形参,它传入的参数是实例对象
self.
即可类名()
创建一个实例时,会自动执行以下操作:
- 为对象在内存中分配空间==>创建对象
- 为对象的属性设置初始值==>初始化方法(
__init__
)
__init__
是对象的内置方法
__init__
方法是专门用来定义一个类具有哪些属性的方法!
class 类1:
def __init__(self, 形参1):
self.属性1 = 形参1
# 实例化
实例1 = 类1(参数1)
- 把希望设置的属性值,定义成初始化函数的参数
- 在方法内部使用 self.属性名 = 形参名 接收外部传递的参数
- 在创建对象时,使用 类名(属性1,属性2…) 调用
class VClass:
"这是一个对象"
def __init__(self, arg):
self.arg = arg
print("%s 初始化成功" % self.arg)
def __del__(self):
print("%s 成功被销毁" % self.arg)
A_class = VClass("A对象")
# 打印在终端的结果显示: 分割线在A对象销毁的上方,说明整个程序结束才销毁 A_class 对象
print("=" * 50)
B_class = VClass("B对象")
del B_class
# 打印在终端的结果显示: 分割线在B对象销毁的下方,说明del提前回收了 B_class 对象
print("*" * 50)
class VClass:
"这是一个对象"
def __init__(self, arg):
self.arg = arg
print("%s 初始化成功" % self.arg)
def __del__(self):
print("%s 成功被销毁" % self.arg)
def __str__(self):
return "%s 的类和内存地址是机密" % self.arg
A_class = VClass("A对象")
print(A_class)
"""A对象 初始化成功
A对象 的类和内存地址是机密
A对象 成功被销毁"""
step1:
将属性和方法 封装到一个抽象的类中step2:
外界使用类创建对象,然后让对象调用方法玩家【火锅制胜】 连续吃了20个果子,然后被【第一名】追杀了七次,无情地被吃掉
class BallGame:
def __init__(self, player, weight):
self.palyer = player
self.weight = weight
print("【用户名】:%s 加入了游戏 【质量】:%.2d" % (self.palyer, self.weight))
def __str__(self):
return "【用户名】:%s 【质量】:%.2d" % (self.palyer, self.weight)
def __del__(self):
if self.palyer == "第一名":
print("【用户名】:%s 【取得了胜利!!!】" % self.palyer)
else:
print("【用户名】:%s 【被吃掉了!!!】" % self.palyer)
def eat(self):
self.weight *= 1.5
print("【用户名】:%s 【吃了一个果子】 【质量变为】:%.2d" % (self.palyer, self.weight))
def accelerate(self):
self.weight *= 0.8
print("【用户名】:%s 【加速!!!】 【质量变为】:%.2d" % (self.palyer, self.weight))
one = BallGame("第一名", 65000)
fire = BallGame("火锅制胜", 10)
# 定义一个 火锅制胜 连续吃了20个果子,然后被第一名追杀了七次,无情地被吃掉的事件
for i in range(20):
fire.eat()
for j in range(7):
fire.accelerate()
del fire
疯狂榨汁机
魔法师学院
None
- None 关键字 表示 什么都没有
- 表示一个 空对象,没有方法和属性,是一个特殊的常量
- 可以将 None 赋值给任何一个变量
- 在属性名或方法名前增加两个下划线,定义的就是私有属性或方法
class female:
__age = 18
def __init__(self, name):
self.name = name
xf = female("小芳")
print("%s 的年龄是 %s" % (xf.name, xf.__age)) # 这里报错了
class female:
__age = 18
def __init__(self, name):
self.name = name
def secret(self):
print(("偷看了%s的隐私,她居然 %s 岁了!哈哈哈!"
% (self.name, self.__age + 38)))
xf = female("小芳")
xf.secret() # 偷看了小芳的隐私,她居然 56 岁了!哈哈哈!
不要使用以下方法访问 私有方法及属性
python
中,并没有真正意义的私有。毕竟是完全开源语法嘛。python中只有伪私有属性和伪私有方法。_类名
实例名._类名__属性或方法名
class female:
__age = 18
def __init__(self, name):
self.name = name
xf = female("小芳")
print("%s 的年龄是 %s" % (xf.name, xf._female__age)) # 小芳 的年龄是 18
实例名._类名__
时IDEL中会有智能提示有哪些伪私有方法属性。class 子类(父类):
class Enchanter:
def fire(self):
print("小火球!")
class HighEnchanter(Enchanter):
def high_fire(self):
print("豪火球之术!!!")
xiaoming = HighEnchanter()
xiaoming.fire()
xiaoming.high_fire()
HighEnchanter
在以下简称son
,Enchanter
在以下简称father
:
son
是father
的子类,father
是son
的父类,son
从father
类继承
son
是father
的派生,father
是son
的基类,son
从father
类派生
- 直接覆盖方法(在子类中定义一个同名方法)
- 对父类方法进行扩展
super().父类方法
来调用父类方法的执行
super
是python
中一个特殊的类
super()
就是使用super
类创建出来的一个实例
- 在重写父类方法时,调用在父类中封装的方法实现
- 代码其他的位置针对子类的需求,编写 子类特有的代码实现
class Enchanter:
def fire(self):
print("小火球!")
class HighEnchanter(Enchanter):
def fire(self):
print("豪火球之术!!!")
# 继承父类的方法,不只是继承同名方法,不同名也可以
super().fire()
xiaoming = HighEnchanter()
xiaoming.fire() # 会显示豪火球之术!!!\r小火球!
父类名.方法名(self)
class Enchanter:
def fire(self):
print("小火球!")
class HighEnchanter(Enchanter):
def hfire(self):
print("豪火球之术!!!")
# 继承父类的方法
New.new(self)
class New():
def new(self):
print("这是一个新的,无关的类")
xiaoming = HighEnchanter()
xiaoming.hfire() # 豪火球之术!!! \r 这是一个新的,无关的类
class 子类名(父类1, 父类2…):
def 子类(父类1, 父类2):......
print(子类.__aro__)
# 终端输出:(, , , )
# 最后一个类叫基类,是所有类的祖宗类
object
是python
为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir
函数查看
object
为基类的类,推荐使用object
为基类的类,不推荐使用python3
中,若不指定父类,会以基类为父类。(python2中若不指定则不会以基类为父类)新式类和经典类在多继承时,方法的搜索顺序不同
python2
和python3
中运行,在定义类时,如果没有父亲,统一继承自object
class 类名(object):…
class Skill(object):
def __init__(self, name):
self.name = name
def conjure(self):
print("豪火球之术!!!")
class HSkill(Skill):
def conjure(self):
print("灭却!火流星")
class Person(object):
def __init__(self, name):
self.name = name
def conjure_skill(self, skill):
print("%s 习得了 %s " % (self.name, skill.name))
skill.conjure()
# skill = Skill("火系法术")
skill = HSkill("火系法术") # 试着把这行注释掉,把上行取消注释
sama = Person("殿下")
sama.conjure_skill(skill)
- 会先为该对象分配空间
- 然后调用初始化方法
__init__
为对象初始化
self.
来调用自己的属性和方法
- 每一个对象都有自己独立的内存空间,保存各自不同的属性
- 多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的引用传递到方法内部(通过self)
python
中,一切皆对象。对象都拥有属性和方法。
类名.
来调用属性和方法class Tools(object):
# 定义一个类属性
count = 0
def __init__(self, name):
self.name = name
# 每一个实例对象都会调用初始化方法,于是放在这里计数。
Tools.count += 1
hammer = Tools("锤子")
axe = Tools("斧头")
hoe = Tools("锄头")
sickle = Tools("镰刀")
saw = Tools("锯子")
print("这个类下有%s个实例" % Tools.count) # 这个类下有5个实例
class Tools(object):
# 需要知道创建了多少实例对象?
count = 0
def __init__(self, name):
self.name = name
# 每一个实例对象都会调用初始化方法,于是放在这里计数。
Tools.count += 1
hammer = Tools("锤子")
print("【通过 类 访问类属性】这个类下有%s个实例" % Tools.count) # 这里会打印出类属性 count = 1
print("【通过 实例 访问类属性】这个类下有%s个实例" % hammer.count) # 这里也会打印出类属性 count = 1
hammer.count = 250 # 利用赋值语句会创建实例属性
print("【通过 实例 访问类属性】这个类下有%s个实例" % hammer.count) # 这里不再打印出类属性,而是实例属性 count = 250
@classmethod
def 类方法名(cls):......
@classmethod
来高数解释器这是一个类方法cls
cls.
来访问类的属性调用其它类的方法class Tools(object):
# 需要知道创建了多少实例对象?
count = 0
def __init__(self, name):
self.name = name
# 每一个实例对象都会调用初始化方法,于是放在这里计数。
Tools.count += 1
@classmethod
def show_count(cls):
print("这个类下有%s个实例" % cls.count)
hammer = Tools("锤子")
Tools.show_count()
@staticmethod
def 静态方法名():......
类名.
就可以调用静态方法, 不需要创建对象class Statue(object):
@staticmethod
def stand():
print("一座雕像就伫立在那")
# 通过 类名. 就可以调用静态方法, 不需要创建对象
Statue.stand()
class Game(object):
top_score = 0 # 记录游戏最高分
def __init__(self, player_name):
self.player_name = player_name
@staticmethod
def show_help():
print("帮助信息:有一“大波”姜氏即将到来,请做好准备")
@classmethod
def show_top_score(cls):
print("游戏的最高分是 : %d" % cls.top_score)
def start_game(self, score):
self.score = score # 记录此次游戏得分
print("%s开始游戏,此次得%d" % (self.player_name, self.score))
# 算出历史最高分,注意,这里并没有cls,调用会报错。直接用类名.
if self.score > Game.top_score:
Game.top_score = self.score
xiaoming = Game("小明")
xiaofang = Game("小芳")
xiaowei = Game("小薇")
xiaoming.show_help() # 查看帮助信息
xiaoming.start_game(66)
xiaofang.start_game(78)
xiaowei.start_game(59)
Game.show_top_score()
- 设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对某一特定问题的成熟的解决方案。(一言以蔽之,设计模式就是一个套路)
- 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码的可靠性
- 目的:让类创建的对象,在系统中只有唯一的一个实例
- 每一次执行
类名()
返回的对象,内存地址是相同的
- 先为对象分配空间 __new__方法
- 后初始化对象 __init__
python
的解释器首先会调用__new__
办法为对象分配空间__new__
是一个由基类提供的内置的静态方法,作用是:
- 在内存中为对象分配空间
python
的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__
方法(就是那个self)class Class(object):
# 记录第一个被创建对象的引用
instance = None
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否为空对象
if cls.instance is None:
# 2.调用基类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls) # 因为new是静态方法,所以要手动传入cls
# 3.返回类属性保存的对象引用
return cls.instance
object1 = Class()
object2 = Class()
print(object1) # <__main__.Class object at 0x02AD9250>
print(object2) # <__main__.Class object at 0x02AD9250>
None
,用于记录单例对象的引用__new__
方法类名()
创建对象时,`python 的解释器都会自动调用两个方法:分配空间方法和初始化方法init_flag
(初始化旗标),标记是否执行过初始化动作,初始值为False
__init__
方法中,判断初始化旗标,若为否则执行初始化动作class Class(object):
instance = None
init_flag = False # 记录是否执行过初始化动作
def __new__(cls, *args, **kwargs):
# 1. 判断类属性是否为空对象
if cls.instance is None:
# 2.调用父类的方法,为第一个对象分配空间
cls.instance = super().__new__(cls)
# 3.返回类属性保存的对象引用
return cls.instance
def __init__(self):
if Class.init_flag: # 如果初始化旗标为否,则执行初始化操作
return
print("初始化方法执行")
Class.init_flag = True
object1 = Class()
object2 = Class()
object3 = Class()
不需要将所有特殊情况 处理地面面俱到,通过异常捕获 可以针对突发事件做集中的处理,从而保证程序的稳定性和健壮性
try
关键字来捕获异常try:
尝试执行的代码
except:
错误出现时执行的代码
语法:except 错误类型
- 多个错误用元组
try:
尝试执行的代码
except 错误类型1:
针对错误类型1,对应的代码处理
except (错误类型2, 错误类型3):
针对错误类型2 和 3,对应的代码处理
python
解释器抛出异常而终止,就可以使用捕获未知错误(捕获未知错误的语法非常固定)except Exception as E:
针对其它错误类型
print("其它未知错误 %s" % E)
python
中针对异常提供的类, as 是用于取别名的关键字,E是随便取的变量名try:
尝试执行的代码
except 错误类型1:
针对错误类型1,对应的代码处理
except (错误类型2, 错误类型3):
针对错误类型2 和 3,对应的代码处理
except Exception as result:
print(result) # 打印其它错误的错误信息
else:
没有异常才会执行的代码
finally:
无论是否有异常,都会执行的代码
def test1():
return 10 / int(input("请输入一个非零数"))
def test2():
return test1()
test2()
请输入一个非零数0
Traceback (most recent call last):
File "d:/@py文件/练习.py", line 7, in
test2()
File "d:/@py文件/练习.py", line 5, in test2
return test1()
File "d:/@py文件/练习.py", line 2, in test1
return 10 / int(input("请输入一个非零数"))
ZeroDivisionError: division by zero
try......except
?利用异常的传递性,在主程序捕获异常即可。在某些特定的业务中:如一般人输入手机号,不等于11位就是错误的。设置密码时,小于8位就是错误的。这时候就需要主动抛出异常
抛出异常:(python
中提供了Exception
类)
- 创建一个
Exception
的对象
2.使用raise
关键字抛出异常对象
def input_passwd():
passwd = input("请输入密码(等于六位)")
if len(passwd) == 6:
return passwd
# 创建一个错误实例,括号内是提示信息
ex = Exception("密码长度不等于六位")
# 抛出错误信息
raise ex
# 输入123来抛出异常,下面来捕获异常
try:
print(input_passwd())
except Exception as R:
print("发生了一个错误【%s】" % R)
"""请输入密码(等于六位)123
发生了一个错误【密码长度不等于六位】"""
,
分隔,导入多个模块,但根据PEP8,每个导入应该独占一行as
取别名 ,这个别名需要满足大驼峰命名法模块名/别名.
来使用模块from 模块名 import 工具名
,来使用部分模块。导入之后,不需要使用模块名/别名.
来使用,可以直接用该方法的名字来使用from 模块名 import *
,可以导入该模块的所有方法,调用时,可以直接使用工具名调用。__file__
可以 查看模块 的 完整路径import django
print(django.__file__)
"C:\Users\User1\AppData\Local\Programs\Python\Python36-32\lib\site-packages\django\__init__.py"
- 定义一个Test文件,输入 print(“这是Test这是Test这是Test”)
- 定义一个main文件,输入 import Test
- 直接运行输出了: 这是Test这是Test这是Test
__name__
:测试模块的代码 只在测试情况下被运行 ,而在 被导入时不会被执行print(__name__) # 输出字符串:__main__
import Test # __name__起作用,输出了模块名:Test
# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
测试使用的代码
# 在代码的最下方:编写有一系列的测试代码
def main():
测试使用的代码
# 根据 __name__ 判断是否执行下方代码
if __name__ == "__main__":
main()
- 目录下有一个特殊的文件
__init__.py
- 包名的命名方式 和变量名一致,采取小写字母加下划线命名法
import 包名
可以一次性导入包 中所有模块__init__.py
中指定对外界提供的模块列表from . import 模块名 # 从当前目录引入模块
from distutils.core import setup
setup(name="包名",
version="版本号",
description="描述信息",
long_description="完整描述信息",
author="作者",
author_email="作者邮箱",
url="主页",
py_modules=["包名.模块名1(不带py后缀)",
"包名.模块名2(不带py后缀)"]) # 选择要分享的模块名
python setup.py build # 如果安装了多个版本python,要发布哪个版本的,就使用哪个版本的解释器
python setup.py sdist
tar -zxvf 压缩包名.tar.gz
sudo python setup.py install
__file__
来查看模块文件的位置python
包/模块sudo pip install 包名/模块名 # 下载
如果有多个版本,py3解释器使用sudo pip3 install
sudo pip uninstall 包名/模块名 # 卸载
- 文本文件:本质上还是二进制文件,可以使用文本编辑软件 查看
- 二进制文件: 保存的文件不是给人直接阅读的,而是提供给其它软件使用的
把大象放进冰箱有几步?三步,打开冰箱,把大象放进去,关上冰箱。
- 将文件内容读入内存
- 将内存内容写入文件
函数/方法 | 说明 |
---|---|
open | 打开文件,并且返回文件对象 |
read | 将文件内容读取到内存 |
write | 将指定内容写入文件 |
close | 关闭文件 |
open
函数的第一个参数是要打开的文件名(文件名区分大小写)
- 如果文件存在,返回该文件
- 如果文件不存在, 跳出异常
- 打开文件之后,要顺手写下关闭文件的代码,然后在中间对文件执行操作
read
方法可以一次性读入并返回 文件的所有内容read
方法后,文件指针 会移动到读取内容的末尾这就是为什么执行完一次读取操作后,再读取一次,会什么也不显示
访问方式 | 说明 |
---|---|
r | 以只读方式打开文件。文件的指针将会放在文件的开头,这是默认模式。如果文件不存在,抛出异常 |
w | 以只写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a | 以追加方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
r+ | 以读写方式打开文件。文件的指针将会放在文件的开头。如果文件不存在,抛出异常 |
w+ | 以读写方式打开文件。如果文件存在会被覆盖。如果文件不存在,创建新文件 |
a+ | 以读写方式打开文件。如果该文件已存在,文件指针将会放在文件的结尾。如果文件不存在,创建新文件进行写入 |
file = open("文件名")
while 1:
line_text = file.readline()
if not line_text:
break
print(line_text)
file.close()
file = open("文件名")
for text in file.readlines()
print(text)
file.close()
file_r = open("源文件名", "r")
file_w = open("目标文件名", "w")
text = file_r.read()
file_w.write(text)
file_r.close()
file_w.close()
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.path.isdir(文件路径) |
- Python 2.x 默认使用 ASCII 编码格式
- Python 3.x 默认使用 UTF-8 编码格式
在 Python 2.x 文件的 第一行 增加以下代码,解释器会以 utf-8 编码来处理 python 文件(官方推荐使用的方法)
# - coding:utf8 -
也可以使用 # coding=utf8
在定义的字符串前加个u(告诉解释器这个字符串是utf-8编码的)
如:u"你好python"
例如:evil("[list]")==>[list]
eval(“1 + 1”)==>2
__import__('os').system('终端命令')
import os
os.system("终端命令")