python零基础入门-面向对象
- 前言
- 面向对象
- 什么是面向对象
- 相关概念
- 类
- 类的定义和使用
- 类的组成与使用
- 对象的创建
- 动态绑定属性和方法
- 小结一下
- 面向对象三大特性 : 封装、继承、多态
- 封装
- 访问限制
- 继承
- 1. 基本认识
- 2. 方法重写
- 3. object类
- 多态
- 静态语言与动态语言
- 特殊方法和特殊属性
- 特殊属性
- 特殊方法
- 类的深拷贝和浅拷贝
- 总结
- 创建用于计算的属性
- 为属性添加安全保护机制
- 派生类中调用基类的__init__()方法定义类属性
- 模块(Modules)
- 概述
- 创建模块
- 自定义模块
- import 导入模块
- 以主程序的形式执行
- 包
- 简介
- 创建包
- 导入包
- python中常用的内置模块
- 第三方模块的安装与运用
- 总结:
前言
该笔记为python自学笔记,基于pycharm的python入门笔记
推荐b站上的一个入门教程链接:【花了2万多买的Python教程全套,现在分享给大家,入门到精通(Python全栈开发教程)-哔哩哔哩】 https://b23.tv/pTSkZdu 笔记中看不懂的地方可以找到b站上相应的内容学习。
面向对象
什么是面向对象
举个栗子
比如你想吃番茄炒蛋,你有两个选择:
- 自己做:买材料→洗菜→切菜→炒菜
- 点外卖:用点餐软件选择商家下单。
其中选择1 就可以理解为面向过程,自己按照流程来做。
选择2就是面向过程,只需要找到这件事的参与者(你,商家),至于这个菜怎么做是商家的事,商家再按步骤做,也就是宏观上找到事物之间的关系(找参与者),参与者从细节上去操作,按照面向过程的方式做完他的事情。
面向对象和面向过程
- 面向过程:
- 面向过程就是具体化、流程化地去解决一个问题,需要一步步分析,一步步实现;
- 面向对象:
- 面向对象(Object Oriented Programming),简称OOP,是一种编程思想。
- 对象是指在现实生活中能够看得见摸得着的具体事物
- 面向对象就是把现实中的事物都抽象成为程序设计中的“对象”,其基本思想是一切皆对象,是一种自下而上的设计语言,先设计组件,再完成拼装。
- 面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,里面有数据也有解决问题的方法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,不管,会用就行了。
- 面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。
相关概念
- 类
类是描述具有相同属性和方法的集合。类就是一组相似事物的统称,(注意:一组而不是一个,相似而不是相同,统称而不是名称。)
2. 对象:类的实例。(比如,类:人类; 你就是一个类的实例,即对象。)对象的属性是静态的,对象的方法是动态的。
3. 方法:类中用def定义。(注意:类之内用def定义的是方法,类之外是函数。)
4. 类变量:定义在类中且在函数之外的变量,在所有实例化对象中公用。
类
先看个例子
class Student: # Student是类名
''' 学生类 '''
# 类属性,直接写在类里的变量称为类属性
native_place = '天津'
# 初始化方法
def __init__(self, name, age, gender): # name,age,gender为实例属性,是所创建的对象都有的
self.name = name # self.name是实体属性,将局部变量的name的值赋值给实体属性
self.age = age
self.gender = gender
# 实例方法(具体的个体)
def info(self): # self一般要写
print('我的名字是', self.name, '年龄是', self.age, '性别是', self.gender)
# 静态方法
@staticmethod
def sm(): # 静态方法里面不允许写self
print('这是静态方法')
# 类方法
@classmethod
def cm(cls): # 类方法中要求传一个cls
print('这是类方法')
# 对象的创建(类的实例化)
# 创建学生对象
stu1 = Student('张三', 20, '男')
stu2 = Student('李四', 19, '男')
print(type(stu1))
print(stu1)
print(stu1.name) # 实例属性
stu1.info() # 对象名.方法名
Student.info(stu1) # 类名.方法名 (与上一行功能相同,都是调用Student中的info方法)
# 类属性的使用方法
print(Student.native_place)
print(stu1.native_place)
# 修改类属性
Student.native_place = '北京'
# 类方法的使用
Student.cm() # 调用的时候不需要传入cls
# 静态方法的使用
Student.sm()
# 动态绑定,为stu1动态绑定年级属性
stu1.grade = '大二'
print(stu1.name, '所在年级为', stu1.grade)
# 动态绑定方法
def eat(): # 在类之外定义的,是函数
print('学生在吃...')
stu1.eat = eat # 绑定了对象之后,就是方法
stu1.eat()
类的定义和使用
# 类的定义
# 语法:
class ClassName:
'''类的帮助信息'''
statement # 包括属性和方法
类的组成与使用
类的组成 | 用法 |
类属性 | 写在类中并且方法体外,可以在所有类的实例之间共享值 |
实例方法 | 实例方法是类的实例对象能调用的方法,self |
静态方法 | 没有默认参数,不能使用类的任何属性或方法,用装饰器 @staticmethod 修饰,使用类名直接访问的方法。 |
类方法 | 默认参数cls,能使用类的任何属性或方法,用装饰器 @classmethod修饰,使用类名直接访问的方法。 |
- 初始化方法:
我们可以通过初始化方法来创建实例对象,每个class只能有一个初始化方法。在创建对象的时候,会自动调用初始化方法进行创建,不需要手动调用。 - 类属性:类属性可以通过两种方式进行调用:
方式 | 注意 |
类名.类属性 | 使用该方式对类属性进行修改,无论之后用哪种方式再次调用该类属性,类属性的值都是修改后的值。 |
对象名.类属性 | 使用该方式对类属性进行修改,只有该对象再次调用的时候类属性的值是修改后的值,其他对象不变。 |
对象的创建
概念
- 对象的创建又称为类的实例化
- 有了实例就可以调用类中的内容
- 语法: 实例名=类名()
stu1 = Student('张三', 20, '男')
动态绑定属性和方法
- python是动态语言,在创建对象后可以动态地绑定属性和方法。
# 动态绑定方法
def eat(): # 在类之外定义的,是函数
print('学生在吃...')
stu1.eat = eat # 绑定了对象之后,就是方法
stu1.eat()
小结一下
面向对象三大特性 : 封装、继承、多态
封装
- 提高程序的安全性
- 将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行操作,在
类对象的外部调用方法。 - 隐藏对象的属性和实现细节,仅对外提供公共访问方式(类比:提款机)。这样,无需关心方法内部的具体实现细节,从而隔离了复杂度。
- 在Python中没有专门的修饰符用于属性的私有,如果该属性不希望在类对象外部被访问,前边使用两个下划线“_”
# 封装
class Student:
def __init__(self, name, age): # 定义构造方法
self.name = name
self.__age = age # age不希望在类外部被使用,所以加两下划线
def show(self):
print(self.name, '的年龄是', self.__age) # 在类内部可以使用age
stu = Student('张三', 20)
stu.show()
# 在类外部使用name和age
print(stu.name)
# print(stu.__age) # 会报错,因为不能直接访问
print(dir(stu)) # 查看stu有哪些属性
print(stu._Student__age) # 在类外部访问需通过 _类名.__属性名
访问限制
做访问限制,是为了程序的健壮性。如果可以从外部对函数里面重要的属性进行任意修改,有可能程序崩溃只是因为一次不经意地参数修改。
# 保护类型属性及其访问
class Swan:
'''天鹅类'''
_neck_swan = '天鹅的脖子很长' # 保护类型属性
def __init__(self):
print('__init__():',Swan._neck_swan) # 访问保护类型的属性
swan = Swan()
print("直接访问:",swan._neck_swan) # 通过实例名访问受保护类型的属性
# 私有类型属性及其访问
class Swan:
'''天鹅类'''
__neck_swan = '天鹅的脖子很长' # 私有类型属性
def __init__(self):
print('__init__():',Swan.__neck_swan) # 访问私有类型的属性
swan = Swan()
swan._Swan__neck_swan = '脖子很长' # 修改私有类型的属性
print("直接访问:",swan._Swan__neck_swan) # 修改后,访问私有类型的属性
#(通过变形改变私有属性的值,并不会影响方法中的调用)
继承
1. 基本认识
- 继承可提高代码的复用性
- 一个类继承一个父类,便可拥有父类的属性和方法
- 如果一个类没有继承任何类,则默认继承object
- 定义子类时,必须在其构造的函数中调用父类的构造函数。
- 语法格式:
class 子类类名(父类类名):
pass
- 子类与父类的理解,如图:
- python 支持多继承,如图:
示例
class Person: # 没有继承任何类,默认继承object, object可以省略不写
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(self.name, self.age)
class Student(Person): # Student作为子类继承Person这个父类
def __init__(self, name, age, stu_no):
super().__init__(name, age) # 调用父类的
self.stu_no = stu_no # 学号
class Teacher(Person):
def __init__(self, name, age, teachofyear):
super().__init__(name, age) # 调用父类的
self.teachofyear = teachofyear # 教龄
# 创建对象
stu1 = Student('张三', 20, 3020226010)
teacher1 = Teacher('李四', 34, 10)
# 调用父类的实例方法
stu1.info() # 只输出姓名和年龄
teacher1.info()
2. 方法重写
- 如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写。
- 子类重写后的方法中可以通过 super().xxx()调用父类中被重写的方法
class Person: # 没有继承任何类,默认继承object, object可以省略不写
def __init__(self, name, age):
self.name = name
self.age = age
def info(self):
print(self.name, self.age)
class Student(Person): # Student作为子类继承Person这个父类
def __init__(self, name, age, stu_no):
super().__init__(name, age) # 调用父类的
self.stu_no = stu_no # 学号
def info(self):
super().info() # 通过 super().info()调用父类中被重写的方法
print('学号:', self.stu_no) # 重写的内容
# 创建对象
stu1 = Student('张三', 20, 3020226010)
# 调用父类的实例方法
stu1.info() # 能输出姓名、年龄和学号
3. object类
- object类是所有类的父类,因此所有类都有object类的属性和方法。
- 使用内置函数dir()可以查看指定对象所有属性
- object有一个str_()方法,用于返回一个对于“对象的描述”,对应于内置函数str()经常用于print()方法,帮我们查看对象的信息,所以我们经常会对_str_()进行重写
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self): # 对_str_()进行重写,用于返回对象的描述
return '我的名字是{},今年{}岁'.format(self.name, self.age)
stu1 = Student('张三', 20)
print(dir(stu1))
print(stu1) # 默认调用__str__()
print(type(stu1))
多态
- 多态就是具有多种形态,指的是即便不知道一个变量所引起的对象到底是什么类型,仍然可以通过这个变量调用方法,在运行过程中根据变量所引用的对象的类型,动态决定调用哪个对象中的方法。
- 父类定义的引用变量可以指向子类的实例对象,提高了程序的拓展性。
class Animal:
def eat(self):
print('动物会吃。')
class Dog(Animal):
def eat(self): # 重写方法
print('狗吃骨头...')
class Cat(Animal):
def eat(self): # 重写方法
print('猫吃鱼...')
class Person:
def eat(self):
print('人吃五谷杂粮...')
# 定义一个函数
def fun(Animal):
Animal.eat()
# 调用函数
fun(Cat())
fun(Dog())
fun(Person()) # Person虽然不是Animal的子类,但是里面也有eat方法。(动态语言的“鸭子类型”)
运行结果:
静态语言与动态语言
静态语言和动态语言关于多态的区别
- 静态语言(如:Java)实现多态的三个必要条件:继承、方法重写、父类引用指向子类对象
- 动态语言(如:python)的多态崇尚“鸭子类型”。当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也像鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为。
特殊方法和特殊属性
特殊属性
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
# 创建实例对象
stu1 = Student('张三', 20)
# 类中的特殊属性
print('实例对象的属性字典:', stu1.__dict__)
print('类对象的属性方法字典:', Student.__dict__)
print('输出对象所属的类:', stu1.__class__)
print('输出类的基类元组:', Student.__bases__)
print('类的层次结构:', Student.__mro__) # 查看继承关系
运行结果:
特殊方法
这块知识点不理解其实也不太影响,对于初学者有一定难度
class Student:
def __init__(self, name, age):
self.name = name
self.age = age
def __add__(self, other):
return self.name + other.name
def __len__(self):
return len(self.name)
# 创建实例对象
stu1 = Student('张三', 20)
stu2 = Student('李四', 19)
print(stu1 + stu2) # 实现了两个对象的加法运算(因为在Student类中编写了__add__()特殊方法)
print(stu1.__add__(stu2)) # 同上
print(len(stu1))
lst = [1, 2, 3, 5]
print(len(lst)) # 在列表中len是内置函数
print(lst.__len__()) # 效果同上
运行结果:
class Student:
def __new__(cls, *args, **kwargs): # 用于创建对象
print('__new__被调用执行了,cls的id值为{}'.format(id(cls)))
obj = super().__new__(cls)
print('创建对象的id为{}'.format(id(obj)))
return obj
def __init__(self, name, age): # 初始化对象的属性
self.name = name
self.age = age
print('__init__被调用了,self的id值为{}'.format(id(self)))
print('object这个类对象的id值为{}'.format(id(object)))
print('Student这个类对象的id值为{}'.format(id(Student)))
# 创建实例对象
stu1 = Student('张三', 20)
print('stu1这个Student类的实例对象的id值为{}'.format(id(stu1)))
运行结果:
- __new__在前去创建对象,__init__在后为对象的实例属性进行赋值,最后将创建的对象放到stu1中存储
- __new__先被调用,__init__后被调用,__new__的返回值(实例)将传递给__init__方法的第一个参数,然后__init__给这个实例设置一些参数。
类的深拷贝和浅拷贝
- 变量的赋值操作:只是形成两个变量,实际上还是指向同一个对象
- 浅拷贝:Python拷贝一般都是浅拷贝,拷贝时,对象包含的子对象内容不拷贝,因此,源对象与拷贝对象会引用同一个子对象
- 深拷贝:使用copy模块的deepcopy函数,递归拷贝对象中包含的子对象,源对象和拷贝对象所有的子对象也不相同
总结
创建用于计算的属性
# 语法:
@prooerty
def methodname(self) # 要转化为属性的方法
block # 一般要通过return语句返回方法结果
class Rect:
def __init__(self,length,width): # 构造方法
self.length = length # 矩形的长
self.width = width # 宽
@property # (装饰器)把方法转换为属性,实现可以计算的属性,
def area(self):
return self.length*self.width # 计算矩形的面积
rect = Rect(800,600) # 创建类的实例
print('面积为:',rect.area) # 输出面积
property属性不能重新赋值
为属性添加安全保护机制
借助装饰器@property,创建可以读取但不能修改值的属性
class TVShow:
'''电视节目类'''
def __init__(self,show):
self.__show = show
@property
def show(self):
return self.__show # 返回私有属性
tvshow = TVShow('正在播放《战狼2》') # 创建类的实例
print('默认:',tvshow.show) # 获取属性值,tvshow不能修改
在一定条件下,让属性值可修改
class TVShow:
'''电视节目类'''
list_film = ['战狼2','红海行动','湄公河行动','我和我的祖国']
def __init__(self,show):
self.__show = show
@property
def show(self):
return self.__show # 返回私有属性
@show.setter # 对以下方法进行转化,让属性可以修改
def show(self,value):
if value in TVShow.list_film: # 允许修改私有属性的条件
self.__show = '您选择了《'+value+'》,稍后播放' # 修改返回值
else:
self.__show ='您点播的电影不存在'
tvshow = TVShow('战狼2') # 创建类的实例
print('正在播放:《',tvshow.show,'》') # 获取属性值
print('您可以从',TVShow.list_film,'中选择要点播的电影')
tvshow.show ='红海行动'
print(tvshow.show)
派生类中调用基类的__init__()方法定义类属性
class Fruit:
def __init__(self,color='绿色'):
Fruit.color = color # 类属性
def harvest(self,color):
print('我原来是',Fruit.color,'的!') # 输出类属性
print('水果已经收获... \n,水果是',color,'的!\n') # 输出形式参数
class Apple(Fruit): # 继承自水果类
color = '红色'
# def __init__(self): # 如果在派生类中不定义构造方法,就会执行基类的构造方法。
print('我是苹果')
class Orange(Fruit):
color = '橙色'
def __init__(self): #在派生类中定义了构造方法
print('我是橘子')
super().__init__() # 在派生类中调用基类的构造方法
apple = Apple() # 创建苹果实例
apple.harvest(apple.color) # 调用基类的harvest()方法
orange = Orange()
orange.harvest(orange.color)
模块(Modules)
概述
- 模块简介
- 一个python程序中包含多个模块
- 模块与函数的关系:一个模块中可以包含多个函数
- Python 中一个以 .py 结尾的文件就是一个模块
- 模块中定义了变量、函数、类、语句等来实现一些类似的功能。
- Python 有很多自带的模块(标准库)和第三方模块,一个模块可以被其他模块引用
- 模块的好处
- 方便其他程序和脚本的导入并使用
- 避免函数名跟变量名冲突
- 提高代码的可维护性和可重用性。
3. 使用模块的好处
- 可以避免函数名和变量名冲突
- 更容易查找代码
- 提高代码的可重用性
- 有选择地使用
创建模块
自定义模块
- 新建一个文件名为 :模块名+ .py
- 不能使用python自带的标准模块名称
import 导入模块
#语法一:
import 模块名 (as 别名) # 别名可加可不加
# 语法二:
from 模块名 import 变量或函数或类
from 模块名 import * # 导入这个模块中的全部定义
print(dir()) # dir函数查看导入了哪些定义
- 导入自定义模块时,推荐使用语法二
模块搜索目录
如果要导入的模块文件和导入它的文件不在同一目录中的解决方法:
- 临时添加要查找的目录
import sys
sys.path.append('E:/change/temp') # 模块所在路径,或E:\\change\\temp
- 增加 .pth 文件 (推荐)
- 在PYTHONPATH环境变量中添加
以主程序的形式执行
if __name__ == '__main__'
- 每个模块的定义中都包括一个记录模块名称的变量__name__,程序可以检查该变量,以确定他们在哪个模块中执行。
- 如果 if__name__ == '__main __'所在模块是被直接运行的,则该语句下代码块被运行;如果所在模块是被导入到其他的python脚本中运行的,则该语句下代码块不被运行。
包
简介
- 包是一个分层次的目录结构,它将一组功能相近的模块组织在一个目录下。
- 包与目录的区别:是否含有__init__.py 文件。(其中__init__.py 可为空也可定义属性和方法。)
- 包的作用:避免模块名重名引发的冲突,起到规范代码的作用。
- Python3.3 之前的版本__init__.py 是包的标识,是必须要有的,之后的版本可以没有。
创建包
导入包
# 语法一:
improt 包名.模块名 (as 别名)
# 语法二:
from 包名 import 模块名
from 包名.模块名 import 变量名
python中常用的内置模块
第三方模块的安装与运用
- 在线安装: pip install 模块名
- 使用:import 模块名
总结: