python3之模块和面向对象
2019-05-16 本文已影响0人
rr1990
一、模块介绍
一个.py文件就是一个模块,模块中可以定义函数、类和变量。
- 模块包括:内置模块、三方模块和自定义模块。
- 模块的优点:
提高代码的可维护性
提高代码的复用性
避免了函数名和变量名的冲突
可以引用其他模块
1. 引用模块
- import语句:
import 模块1[, 模块2..., 模块n]
import 模块名.函数名
import 模块名.变量名
- from...import...语句
from 模块名 import 方法1[, 方法2, ...方法n]
从模块中导入指定的方法到当前命名空间
from 模块名 import *
把模块中的所有内容全部导入到当前命名空间
- 导入模块指定别名
from 模块名 import 方法1 as name
从模块中导入方法1并指定别名为name,调用时name.XXX或name(XXX)
- 引用模块的方法注意事项:
同一模块无论被import多少次,为了防止重复引入,均只会被引入一次。
可一次性引入多个模块,不同模块之间用逗号分隔
2. 模块中特殊属性
2.1. __name__
属性
模块就是一个可执行的.py文件,一个模块被另一个程序引入。
如果不想让模块中的某些代码执行,可以用__name__
属性。每个模块都有一个__name__
属性。
当前文件如果为程序的入口文件时,则__name__ == "__main__"
。当__name__
属性的值为"__main__"
时,则表明该模块自身在执行;否则被引用其他文件。
3. 包
防止模块命名冲突,引入按目录来组织模块的方法,称为包package。
引入包后,只要顶层的包与其他文件不冲突,此包下的模块就不会与其他文件发生冲突。
每个包下必须存在一个__init__.py
文件,此文件内容可为空。
__init__.py
文件的作用:避免一些滥竽充数的名字,且目录下只有存在此文件才可认为此目录为一个包。
4. 安装第三方模块
(1)前提:安装python时安装有pip
查看是否安装有pip方法,命令行输入pip -V
C:\Users\admin>pip -V
pip 18.1 from d:\programs\python\python36\lib\site-packages\pip (python 3.6)
(2)安装三方模块
- pip install 模块名
安装模块时有时会报错,提示升级pip版本。 - pip install --upgrade pip:升级pip版本
(1)安装Pillow模块:
C:\Users\admin>pip install Pillow
Requirement already satisfied: Pillow in d:\programs\python\python36\lib\site-packages (5.3.0)
You are using pip version 18.1, however version 19.1.1 is available.
You should consider upgrading via the 'python -m pip install --upgrade pip' command.
---报错,提示升级pip
(2)升级pip
遇到过升级pip后无法找到pip模块:
File "D:\Programs\Python\Python36\Scripts\pip.exe\__main__.py", line 5, in <module>
ModuleNotFoundError: No module named 'pip'。但实际上有pip文件。
解决方案:
首先执行:python -m ensurepip
再升级pip:python -m pip install --upgrade pip
(3)查看pip安装的模块
- pip list:列出所有的模块信息
- pip show -V 模块名 | findstr Version:window下获取某个模块的版本号
- pip show -V 模块名 | grep Version:Linux下获取某个模块的版本号
二、面向对象
1. 面向过程和面向对象的介绍
1.1. 面向过程:
- 程序从上到下顺序执行的代码 。
- 面向过程强调的是功能行为
1.2. 函数式:
- 将功能代码封装到函数中,无需重复编写,仅调用函数。
1.3. 面向对象:
- 把数据及对数据的操作方法放在一起,作为一个相互依赖的整体,这就是对象。
- 把对同类对象抽象出其共性,形成类。
类中的大多数数据,只能用本类的方法进行处理;
类通过一个简单的外部接口与外界发生联系,对象与对象之间通过消息进行通信;
程序流程由用户在使用中决定; - 面向对象是基于面向过程的。
- 面向对象是将功能封装成对象,强调的是具备功能的对象
- 面向对象可以将复杂的事情简单化
- 面向对象OO(object-oriented)的三大特性:多态、封装、继承。
2. 类
2.1. 类的介绍
- 类是用来描述事件的属性和行为动作。
属性:对应类中的成员变量;
行为:对应类中成员方法或功能;
定义类就是定义类中的成员(成员变量和成员方法),拥有相同属性和行为的对象都可以使用此类。
设计类包括:类名、属性、行为(方法/功能)。
2.2. 创建类
- 类是一种数据类型,本身并不占内存空间。用类创建实例化对象(变量),对象占内存空间。
- 类以class 关键词开头,后接类名(类名通常是以大写开头的单词)
- 定义有继承的类,则在类名后紧跟以括号括起来的父类名,表示该类是从此父类继承下来的。一个类可以继承多个类,类之间用逗号分隔。
若无合适的继承类,可使用object类(object类是所有类最终都会继承的类。)---object类又称基类和超类。 - 类中定义的函数,第一个参数永远是self,并且调用时不用传递该参数。self代表类的实例,即某个对象。(注:没有在类中定义的函数,参数中无需添加变量self)
-----创建类的格式:
class 类名(父类1, 父类2...):
属性
行为
-----访问类
1. 使用类实例化对象
实例化对象格式:对象名 = 类名(参数列表)
注:类没有参数,类名后的小括号也不可省略。
2. 访问属性
访问属性格式:对象名.属性名
赋新值:对象名.属性名 = 新值
3. 访问方法
访问方法格式:对象名.方法名(参数列表)
# 创建不含继承的类
class Student1:
name = "zhangsan" # 属性
def getScore(self, score): # 方法
print("这个学生的分数是:", score)
# 获取类的实例化对象
student1 = Student1()
# 访问属性
getName = student1.name
print(getName) # zhangsan
# 访问方法
student1.getScore(90) # 这个学生的分数是: 90
# 创建含继承的类
class Student2(object):
name = "zhangsan" # 属性
def getScore(self, score): # 方法
print("这个学生的分数是:", score)
# 获取类的实例化对象
student2 = Student2()
# 访问属性
getName = student2.name
print(getName) # zhangsan
# 访问方法
student2.getScore(80) # 这个学生的分数是: 80
2.3. 构造函数
- 构造函数
__init__()
:即对象的初始化,使用类创建对象的时候自动调用。
-----创建类的格式:
class 类名(父类1, 父类2...):
def __init__(self, 属性1, 属性2,...): # 属性
self.属性1 = 属性1
self.属性2 = 属性2
...
def 方法函数(self, 方法参数1, 方法参数2, ...)
行为
-----访问类
1. 使用类实例化对象
实例化对象格式:对象名 = 类名(属性参数列表)
注:类没有参数,类名后的小括号也不可省略。
2. 访问属性
访问属性格式:对象名.属性名
赋新值:对象名.属性名 = 新值
3. 访问方法
访问方法格式:对象名.方法名(方法参数列表)
# 创建含构造函数的类
class Student3(object):
def __init__(self, name):
self.name = name
def getScore(self, score):
print("这个学生的分数是:", score)
# 获取类的实例化对象
student3 = Student3("liNing")
# 访问属性
print(student3.name) # liNing
# 访问方法
student3.getScore("70") # 这个学生的分数是: 70
2.4. self介绍
- self代表类的实例,而非类。
哪个对象调用方法,那么该方法中的self就代表哪个对象。
self不是python的关键字,将对象方法中的self换成其他标识符也可以,但一般常规是用self。
1. -----类的内部访问属性和方法-----
访问属性格式:self.属性名
访问方法格式:self.方法名(参数)
获取类名:self.__class__
2. -----类的外部访问属性和方法-----
访问属性格式:类的实例化对象.属性名
访问方法格式:类的实例化对象.方法(参数)
"""类的内部和外部分别获取属性和方法"""
class Student4(object):
def __init__(self, name):
self.name = name
def getScore(self, score):
return score
def getStudentInfo(self, score):
print("姓名:%s,分数:%d" % (self.name, self.getScore(score))) # 类的内部访问属性和方法
def getClassName(self): # 获取类名
print(self.__class__)
def createObject(self): # 创建对象
newObject = self.__class__("LiHong")
print(newObject)
# 获取类的实例化对象
student4 = Student4("LiMing")
# 类的外部访问属性
print(student4.name) # LiMing
# 类的外部访问方法
student4.getStudentInfo(80) # 姓名:LiMing,分数:80
student4.getClassName() # <class '__main__.Student4'>
student4.createObject() # <__main__.Student4 object at 0x000000000234E240>
2.5. 析构函数
- 析构函数
__del__()
:释放对象时自动调用,一般不需要手动添加析构函数。
只有当对象的引用数为0时才会自动调用析构函数来回收资源。
在函数中定义的对象,会在函数结束时自动释放,以减小内存空间的浪费。
del 对象:手动删除对象时会调用析构函数。对象释放后不可再访问此对象。 - 构造函数和析构函数的区别
创建一个对象时一定会调用构造函数;
对象结束时一定会调用析构函数。
class Student5(object):
def __init__(self, name):
self.name = name
def getScore(self, score):
return score
def getStudentInfo(self, score):
print("%s的分数是%d" % (self.name, self.getScore(score)))
def __del__(self):
print("这是个析构函数")
student5 = Student5("LiMing")
print(student5.getScore(80))
student5.getStudentInfo(80)
# del student5 # 删除student5对象
# student5.getStudentInfo() # 删除student5对象后,不可再使用此对象,否则会提示对象未被定义
print("代码结束")
"""运行结果:
80
LiMing的分数是80
代码结束
这是个析构函数
"""
2.6. 类中特殊方法__str__()
和__repr__()
-
__str__()
:在调用print打印对象时自动调用,是给用户使用的,是一个用来描述对象的方法。 -
__repr__()
:在python解释器中输入对象名时,调用的方法。
注意:在没有__str__
,但有__repr__
时,调用print打印对象时会自动调用__repr__
。
优点:当一个对象的属性值很多,并且都需要打印多次时,可将属性信息添加到此方法中,简化代码。
__str__()
和__repr__()
方法使用任意一种即可,一般使用__str__()
。
class Student6(object):
def __init__(self, name, sex, score):
self.name = name
self.sex = sex
self.score = score
def getStudentInfo(self):
print("%s的分数是%d" % (self.name, self.score))
def __str__(self): # 这是个__str__函数
return "name=%s, sex=%s, score=%d" % (self.name, self.sex, self.score)
def __repr__(self): # 这是个__repr__函数
return "name=%s, sex=%s, score=%d" % (self.name, self.sex, self.score)
# 获取类的实例化对象
student6 = Student6("LiMing", "female", 80)
# 打印对象
print(student6) # name=LiMing, sex=female, score=80
2.7. 类中私有属性
- 在属性前加两个下划线,则表示该属性为私有属性。在类的外部不可使用或访问私有属性,但在类的内部可以正常访问。
- 若在类的外部直接访问私有属性,python解释器会把该私有属性名变为
_类名__私有属性名(即_Person1__money)
,且不同的解释器将该私有属性名修改的名字可能不同,故不允许使用类直接访问私有属性。-----私有属性并不是绝对私有 - 可通过自定义的方法修改私有属性的值和获取私有属性的值-----类的外部间接访问私有属性。
私有属性一般会通过添加set方法和get方法来访问。 - 在属性前后均加两个下划线,则该属性并不是私有属性,而是特殊变量,类内外部均可直接访问。
"""外部无法访问私有属性"""
class Person1(object):
def __init__(self, name, sex, money):
self.name = name
self.sex = sex
self.__money = money
def getInfo(self):
print("%s拥有%d钱" % (self.name, self.__money)) # 内部可以使用私有属性__money
person1 = Person1("LiMing", "female", 1000)
# print(person1.__money) # 报错,外部无法访问私有属性__money
# person1.__money = 100 # 外部也可以访问此私有属性,但此私有属性名已发生改变,故不允许此用法
"""外部修改和获取私有属性的方法"""
class Person2(object):
def __init__(self, name, sex, money):
self.name = name
self.sex = sex
self.__money = money
def setMoney(self, money): # 修改私有属性的值
if money < 0:
money = 0
self.__money = money
def getMoney(self): # 获取私有属性的值
return self.__money
person2 = Person2("LiMing", "female", 1000)
print(person2.getMoney()) # 1000
person2.setMoney(200)
print(person2.getMoney()) # 200
3. 面向对象的简单例子
"""用例:人开车
分析:需要创建三个类,人、车、油箱
人---类名:Person;属性:car;方法:开车、加油
车---类名:Car;属性:油箱;方法:车跑
油箱---类名:OilBox;属性:油
"""
class OilBox(object):
"""创建一个油箱"""
def __init__(self, oil):
self.oil = oil
class Car(object):
"""创建一个车"""
def __init__(self, oilBox):
self.oilBox = oilBox # 油箱
def run(self):
"""车执行跑操作"""
if self.oilBox.oil == 0:
print("没有油了")
else:
self.oilBox.oil -= 10
print("剩余油量", self.oilBox.oil)
class Person(object):
"""创建一个人"""
def __init__(self, car):
self.car = car
def driver(self):
"""人要开车, 车要跑"""
self.car.run()
def addOil(self, oil):
"""给车加油"""
self.car.oilBox.oil = oil
"""使用面向对象实现人开车操作"""
from Examples.Example1.person import Person
from Examples.Example1.car import Car
from Examples.Example1.oilBox import OilBox
oilBox = OilBox(100) # 创建一个油箱对象
car = Car(oilBox) # 创建一个车对象
person = Person(car) # 创建一个人对象
person.driver() # 人开车
person.addOil(200) # 人加油
person.driver()
三、继承
1. 继承定义
- 类A继承类B,则继承者(A)称为子类(派生类),被继承者(B)称为父类(基类或超类)。
object类是所有类的父类(基类或超类)。
子类可以直接使用父类中的属性和方法,而不用重新编写代码;
子类可以重新定义父类中的某个属性和重写某个方法,从而覆盖父类中原有的属性和方法;
子类可以添加新的属性和方法。 - 继承的优点:简化代码,减小冗余;提高了代码的健壮性和安全性。
- 继承的缺点:继承的耦合性很高(因为父类发生改变,子类也会发生改变)。
耦合性:模块之间相互联系的紧密程度,越紧密耦合性越强;
内聚性:一个模块内部各个元素彼此结合的紧密程度。
耦合与内聚是描述类与类之间的关系的,耦合性越低内聚性越高,代码越好。 - 继承分为:单继承、多继承。
2. 单继承
单继承即仅继承一个类。
2.1. 子类属性的继承和重写
- 子类中无构造函数
子类中若无构造函数,则子类也可直接使用父类的属性。 - 子类中构造函数的重写
父类中存在构造函数,子类中也有构造函数,则子类的构造函数需要调用父类的构造函数。
子类构造函数中也可添加有新的属性。
子类中构造函数调用父类构造函数有两种方法:
第一种:父类.__init__(self, 参数1, 参数2, ...)
第二种:super(子类, self).__init__(参数1, 参数2, ...)
2.2. 子类方法的继承和重写
- 子类中无与父类相同的方法,则子类可以直接使用父类的方法。
- 子类中存在与父类相同的方法,则子类的方法会覆盖父类的方法。
2.3. 子类继承单个父类的例子
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self, food):
print("%s正在吃%s" % (self.name, food))
"""------子类使用父类的属性和方法------"""
class Teacher1(Person):
pass
# 子类使用父类的属性
teacher1 = Teacher1("A", 20)
print(teacher1.name, teacher1.age) # A 20
# 子类使用父类的方法
teacher1.eat("rice") # A正在吃rice
"""------子类重写父类的属性、直接使用父类的方法------"""
class Teacher2(Person):
def __init__(self, name, age):
"""子类重写父类属性"""
# Person.__init__(self, name, age) # 重写构造函数的第一种方法
super(Teacher2, self).__init__(name, age) # 重写构造函数的第二种方法
# 子类使用父类的属性
teacher2 = Teacher2("A", 20)
print(teacher2.name, teacher2.age) # A 20
# 子类直接使用父类的方法
teacher2.eat("fish") # A正在吃fish
"""------子类重写父类属性且添加新的属性,子类重写父类方法------"""
class Teacher3(Person):
def __init__(self, name, age, sex):
"""子类重写父类属性"""
# Person.__init__(self, name, age) # 重写构造函数的第一种方法
super(Teacher3, self).__init__(name, age) # 重写构造函数的第二种方法
self.sex = sex
def eat(self, food):
"""子类重写父类方法"""
print("%s is eating %s" % (self.name, food))
teacher3 = Teacher3("A", 20, "female")
print(teacher3.name, teacher3.age, teacher3.sex) # A 20 female
# 子类直接使用父类的方法
teacher3.eat("fish") # A is eating fish
3. 多继承
多继承即同时继承多个类。
3.1. 子类属性的继承和重写
- 子类需要重写父类的构造函数,且也可添加属于自己的新属性。
子类构造函数中也可添加有新的属性。
父类1.__init__(self, 参数1, 参数2, ...)
父类2.__init__(self, 参数1, 参数2, ...)
...
父类n.__init__(self, 参数1, 参数2, ...)
3.2. 子类方法的继承和重写
- 子类继承多个父类,且父类中存在相同的方法名。
若子类中未重写此方法,子类使用此方法时,默认调用的是括号中排在前面的父类方法;
若子类中重写有此方法,则会使用子类中的方法。
3.3. 子类继承多个父类的例子
- 子类Person既是程序员又是厨师
class Programmer(object): # 程序员
def __init__(self, language):
self.language = language
def writeCode(self):
print("用%s语言写代码" % self.language)
def phone(self):
print("程序员在打电话")
class Chef(object): # 厨师
def __init__(self, food):
self.food = food
def cooking(self):
print("烹饪%s" % self.food)
def phone(self):
print("厨师在打电话")
"""------子类继承多个父类,子类重写父类的属性------"""
class Person1(Programmer, Chef):
def __init__(self, name, age, language, food):
"""子类重写父类属性"""
Programmer.__init__(self, language)
Chef.__init__(self, food)
self.name = name
self.age = age
# 创建类的实例化对象
person1 = Person1("A", 18, "Python", "fish")
# 对象的属性
print(person1.name, person1.age, person1.language, person1.food) # A 18 Python fish
# 子类使用父类方法
person1.writeCode() # 用Python语言写代码
person1.cooking() # 烹饪fish
# 父类存在相同的方法,子类使用时默认使用括号中排名在前的方法
person1.phone() # 程序员在打电话
"""------子类继承多个父类,子类重写父类的属性且重写父类方法------"""
class Person2(Programmer, Chef):
def __init__(self, name, age, language, food):
"""子类重写父类属性"""
Programmer.__init__(self, language)
Chef.__init__(self, food)
self.name = name
self.age = age
def phone(self):
print("%s在打电话" % self.name)
person2 = Person2("A", 20, "Python", "fish")
person2.phone() # A在打电话
四、多态
多态:一种事件的多种形态。意味着即使不知道变量指向什么对象,也能够对此对象执行操作,且操作的行为跟随对象所必的类型而变。
class Animal(object):
def __init__(self, name):
self.name = name
def run(self):
print("%s在跑步" % self.name)
"""采用多态方式实现"""
class Person(object):
def leadAnimalRun(self, animal): # animal采用的多态方式
print("人训练动物跑步")
animal.run()
person = Person()
cat = Animal("Cat")
person.leadAnimalRun(cat)
dog = Animal("Dog")
person.leadAnimalRun(dog)
五、对象属性和类属性
一个类所包含的属性有:对象属性和类属性。
1. 类属性
- 在类中,方法外面定义的变量称为类属性。
在类的内部和外部,类属性值均可以通过类或对象来访问,但只能通过类来修改。
1.1. 访问类属性
- 通过类访问类属性
在类的内部和外部访问:类名.类属性
- 通过对象访问类属性
在类的内部访问:self.类属性
在类的外部访问:实例化对象.类属性
1.2. 修改类属性
-
类名.类属性 = 新值
通过类来修改类属性值,由于按全局变量的方式修改,则整个类中的此属性值均发生变化。 -
self.类属性 = 新值
或实例化对象.类属性 = 新值
此方法相当于把类属性变成实例化对象属性,相当于局部变量,仅会对当前对象生效,对此类创建的其他对象不生效。
2. 对象属性
- 在类中,
__init()__
方法中定义的属性称为对象属性。
在类的内部访问:self.对象属性
在类的内部修改值:self.对象属性 = 新值
在类的外部访问:实例化对象.对象属性
在类的外部修改值:实例化对象.对象属性 = 新值
- 可动态的给对象添加新的对象属性,但此对象只对当前对象生效,对此类创建的其他对象无用。
- 在同一类中,若类属性名与对象属性名相同,则对象属性的优先级高于类属性。
class Person1(object):
count = 0 # 类属性
def __init__(self, name): # 对象属性
self.name = name
def testClassAttribute(self):
"""通过类修改类属性值"""
Person1.count = 50 # 通过类名的方式给类属性值赋新值,相当于全局变量
print(Person1.count) # 50
print(self.count) # 50
"""通过对象修改类属性值"""
self.count = 60 # 通过对象的方式给类属性值赋新值,相当于局部变量,仅对当前对象生效
print(Person1.count) # 50
print(self.count) # 60
def testObjectAttribute(self):
"""对象属性"""
self.name = 'B'
print(self.name) # B
person1 = Person1("A")
person1.testClassAttribute()
print(Person1.count) # 50
print(person1.count) # 60
person1.testObjectAttribute()
person1.name = "C" # 类外部修改对象属性值
person1.age = 20 # 类外部新建新的属性,并赋值
print(person1.name, person1.age) # C 20
六、动态给实例添加属性和方法
1. 动态给实例添加属性和方法
- 动态给实例添加属性
实例对象.新属性 = 属性值 - 动态给实例添加方法
先导入模块from types import MethodType
实例对象.新方法 = MethodType(方法名, 实例对象)
class Person(object):
pass
person = Person()
"""给对象动态添加属性"""
person.name = "LiMing"
print(person.name) # LiMing
"""给对象动态添加方法"""
from types import MethodType
def getAge(self, age):
print("%s的年龄:%d" % (self.name, age))
person.ageMethod = MethodType(getAge, person)
person.ageMethod(20) # LiMing的年龄:20
2. 限制对象中添加的动态属性
定义类时定义一个特殊的属性__slots__
,此属性可限制动态添加的属性。
from types import MethodType
class Student:
__slots__ = ("name", "age", "sex", "scoreInfo") # 表示给Student类添加属性时仅可添加name、age、sex、scoreInfo
student = Student()
student.name = "LiHong"
print(student.name) # LiHong
# student.work = "Doctor" # 会报错提示此对象无work属性
# print(student.work)
def getScore(self, score):
print("%s的分数:%d" % (self.name, score))
student.scoreInfo = MethodType(getScore, student)
student.scoreInfo(90) # LiHong的分数:90
七、运算符重载
对两个对象执行运算操作,则提示类型不支持(TypeError: unsupported operand type(s)),因此需要借助运算符重载。
1. 常用的运算符重载
构造函数__init__
解析函数__del__
打印函数__repr__、__str__
函数调用__call__
函数属性__getattr__、__setattr__、__delattr__
数字运算符函数:__add__(self, other)、__sub__(self, other)、__mul__(self, other)、__div__(self, other)
...
class Student:
def __init__(self, score):
self.score = score
def __add__(self, other):
return Student(self.score + other.score)
def __str__(self):
return "score=%d" % self.score
student1 = Student(70)
student2 = Student(80)
print(student1 + student2) # score=150
print(student1.__add__(student2).score) # 150
2. __call__
介绍:
__call__
是一个可调用对象,而函数和类都属于可调用对象。
通过__call__
方法可以将一个类实例化对象变成一个可调用对象。使用__call__
还可以
class Person:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def __call__(self, name, sex):
self.name = name
self.sex = sex
person = Person("LiMing", 20, "female") # 实例化对象
person("LiHong", "male") # 将实例变为可调用的对象
print(person.name, person.sex, person.age) # LiHong male 20