python进阶之面向对象编程

发布时间 2024-01-03 19:44:39作者: Xiao0101

引入:编程范式/思想

image

image

什么是面向过程

介绍:
​	核心是“过程”二字,过程就是“流水线”;过程终极奥义是将程序 流程化
优点:
​	将程序流程化,进而程序的设计会变得简单化
缺点:
​	可扩展性差

什么是面向对象

介绍:
​	核心是“对象”二字,对象就是“容器”,用来盛放数据与功能;对象终极奥义是将程序进行高度整合
优点:
​	提升程序的解耦合程度,进而增强程序的可扩展性
缺点:
​	设计复杂

一、初识对象

使用对象组织数据。在程序中可以做到和生活中一样,设计表格、生产表格、填写表格的组织形式的。
1.在程序中设计表格:--设计类

class Student:
    name = None #记录学生姓名

2.在程序中打印生产表格:--创建对象

#基于类创建对象
stu_1 = Student()
stu_2 = Student()

3.在程序中填写表格:--对象属性赋值

stu_1.name = "张三" # 为对象赋予name属性
stu_2.name = "张四"

演示类的创建

1.设计一个学生类 :类比生活中设计一张记录学生信息的登记表

class Student:
    name = None
    gender = None
    nationality = None
    native_place = None
    age = None

2.创建一个对象(类比生活中打印一张登记表)

stu_1 = Student()
stu_2 = Student()

3.对象属性进行赋值(类比生活中:填写表单)

stu_1.name = "张三"
stu_1.gender = '男'
stu_1.nationality = "中国"
stu_1.native_place = "山东省"
stu_1.age = "23"

4.获取对象中记录的信息

print(stu_1.name)
print(stu_1.gender)
print(stu_1.nationality)
print(stu_1.native_place)
print(stu_1.age)

二、成员方法

1、类的定义和使用语法:

-class是关键字,表示要定义类了
-类的属性,即定义在类中的变量(成员变量) 
-类的行为,即定义在类中的函数(成员方法)
##语法:
class  类名称:
    类的属性
    
	类的行为

创建类对象的语法: 对象 = 类名称()

2、成员变量和成员方法的使用:

什么是类的行为(方法)?
class Student:
    name = None   # 学生的姓名
    age = None    # 学生的年龄
    
    def say_hi(self):    # 函数是类的方法
        print(f'Hello,world!我是{self.name}')
stu = Student()
stu.name = 'xiao'
stu.say_hi()   # 输出:Hello,world!我是xiao

可以看出  类中可以:
	定义属性用来记录数据
     name = None   # 学生的姓名
     age = None    # 学生的年龄
	定义函数用来记录行为
     def say_hi(self)
其中:
	类中定义的属性(变量)称作-- 成员变量
	类中定义的行为(函数)称作-- 成员方法
在类中定义成员方法和定义函数基本一致,但仍有细微区别:
def 方法名(self,形参1,...,形参N)
	方法体
可以看到,在方法定义的参数列表中,有一个:self关键字
self关键字是成员方法定义的时候,必须填写的。
· 它用来表示类对象自身的意思 
· 当我们使用类对象调用方法的是,self会自动被python传入
· 在方法内部,想要访问类的成员变量,必须使用self

3、self关键字
self --尽管在参数列表中,但是传参的时候可以忽略它

class Student:
    name = None
    def say_hi(self):
        print('Hello world')
    def say_hi2(self,msg):
        print(f'Hello,大家好,{msg}')
stu = Student()
stu.say_hi()                 # 调用的时候无需传参
stu.say_hi2('很高兴认识大家')  # 调用的时候,需要传msg参数

演示面向对象类中的成员方法定义和使用

# 定义一个带有成员方法的类
class Student:
    name = None
 
 
    def say_hi(self):
        print(f"大家好,我是{self.name}")
 
 
    def say_hi2(self,msg):
        print(f"大家好,我是{self.name},{msg}")
stu_1 = Student()
stu_1.name = "李四"
stu_1.say_hi2("哎哟不错呀")
 
 
stu_2 = Student()
stu_2.name = "李wy"
stu_2.say_hi()

三、类和对象

现实世界的事物也有属性和行为,类也有属性和行为;
实用程序中的类,可以完美的描述现实世界的事物。那么为什么要创建对象才能使用呢?
类只是一种程序内的“设计图纸”,需要基于图纸生产实体(对象),才能正常工作 。这种思想就称之为:面向对象编程

使用类和对象描述现实事物

image

在程序中通过类来描述;

image

基于类创建对象

image

这就是面向对象编程: --设计类,基于类创建对象,由对象做具体的工作 。(调用类的过程又称之为实例化)

四、构造方法 (__init__)

1、属性(成员变量)的赋值

class Student:
    name = None
    age = None
    tel = None
student1 = Student()
student1.name = 'xiao'
student1.age = 18
student1.tel = '13365273678'

student2 = Student()
student2.name = 'quan'
student2.age = 100
student2.tel = '13232324567'
上述代码中,为对象的属性赋值需要依次进行,略显繁琐,有没有更加高效的方式,能够一行代码就能完成呢?
思考:Student()这个括号能否像函数(方法)那样,通过传参的形式对属性赋值呢
答案是可以,需要使用构造方法:__init__()

2、 构造方法:
Python类可以使用__init__()方法,称之为构造方法
可以实现:--在创建类对象(构造类的时候)
-- 构建类的时候_-init方法会自动执行
-- 构建类时传入的参数会自动传递给__init__方法使用

class Student:
    name = None
    age = None
    tel = None  #可以省略
    
    def __init__(self,name,age,tel):
        self.name = name
        self.age = age
        self.tel = tel
        print('Student类创建了一个对象')
        
stu = Student('xiao',18,'12345678945')

3、构造方法注意事项
-- 重要的事情说三遍,构造方法名称:__init__ __init__ __init__ , 千万不要忘记init前后都有2个下划线
-- 构造方法也是成员方法,不要忘记在参数列表中提供:self
-- 在构造方法内定义成员变量,需要使用self关键字

def __init__(self, name, age,tel):
    self.name = name  # stu1.name = 'xiao'
    self.age = age    # stu1.age = 18
    self.tel = tel    # stu1.tel = '12345678998'
    print('Student类创建了一个对象')

这是因为:变量是定义在构造方法内部,如果要成为成员变量,需要用self来表示。

4、构造方法案例

"""
设计一个类,记录学生的:
姓名、年龄、地址,这3类信息
通过for循环,配合input输入语句,并使用构造方法,完成学生信息的键盘录入
输入完成后,使用print语句,完成信息的输出
"""
class Student:
    name = None
    age = None
    addres = None   # 可以省略
 
    def __init__(self,name,age,addres):
        self.name = name
        self.age = age
        self.addres = addres
 
for x in range(1,10):
    name_in = input("请输入学生姓名:")
    age_in = input("请输入学生年龄:")
    address_in = input("请输入学生地址:")
    stu = Student(name_in,age_in,address_in)
    print(f"当前录入第{x}位学生信息,总共需要录入10位学生信息")
    print(f"学生{x}信息录入完成,信息为:【学生姓名:{stu.name},年龄:{stu.age},地址:{stu.addres}】")
 
 
print("所有学生的信息录入完毕")

五、面向对象编程

5.1 类的定义与实例化

我们以开发一个清华大学的选课系统为例,来简单介绍基于面向对象的思想如何编写程序

面向对象的基本思路就是把程序中要用到的、相关联的数据与功能整合到对象里,然后再去使用,但程序中要用到的数据以及功能那么多,如何找到相关连的呢?我需要先提取选课系统里的角色:学生、老师、课程等,然后显而易见的是:学生有学生相关的数据于功能,老师有老师相关的数据与功能,我们单以学生为例,

# 学生的数据有
学校
名字
年龄
性别

# 学生的功能有
选课

详细的

# 学生1:
    数据:
        学校=清华大学
        姓名=李建刚
        性别=男
        年龄=28
    功能:
        选课

# 学生2:
    数据:
        学校=清华大学
        姓名=王大力
        性别=女
        年龄=18
    功能:
        选课

# 学生3:
    数据:
        学校=清华大学
        姓名=牛嗷嗷
        性别=男
        年龄=38
    功能:
        选课

我们可以总结出一个学生类,用来存放学生们相同的数据与功能

# 学生类
    相同的特征:
        学校=清华大学
    相同的功能:
        选课

基于上述分析的结果,我们接下来需要做的就是在程序中定义出类,然后调用类产生对象

class Student: # 类的命名应该使用“驼峰体”

    school='清华大学' # 数据

    def choose(self): # 功能
        print('%s is choosing a course' %self.name)

类体最常见的是变量的定义和函数的定义,但其实类体可以包含任意Python代码,类体的代码在类定义阶段就会执行,因而会产生新的名称空间用来存放类中定义的名字,可以打印Student.__dict__来查看类这个容器内盛放的东西

print(Student.__dict__)
{..., 'school': '清华大学', 'choose': <function Student.choose at 0x1018a2950>, ...}

调用类的过程称为将类实例化,拿到的返回值就是程序中的对象,或称为一个实例

stu1=Student() # 每实例化一次Student类就得到一个学生对象
stu2=Student()
stu3=Student()

如此stu1、stu2、stu3全都一样了(只有类中共有的内容,而没有各自独有的数据),想在实例化的过程中就为三位学生定制各自独有的数据:姓名,性别,年龄,需要我们在类内部新增一个__init__方法,如下

class Student:
    school='清华大学'

    #该方法会在对象产生之后自动执行,专门为对象进行初始化操作,可以有任意代码,但一定不能返回非None的值
    def __init__(self,name,sex,age):
        self.name=name  # 前面的name是类的属性,后面的name是变量名
        self.sex=sex
        self.age=age

    def choose(self): 
        print('%s is choosing a course' %self.name)

然后我们重新实例出三位学生

stu1=Student('李建刚','男',28)
stu2=Student('王大力','女',18)
stu3=Student('牛嗷嗷','男',38)

单拿stu1的产生过程来分析,调用类会先产生一个空对象stu1,然后将stu1连同调用类时括号内的参数一起传给Student.__init__(stu1,’李建刚’,’男’,28)

def __init__(self, name, sex, age):
    self.name = name  # stu1.name = '李建刚'
    self.sex = sex    # stu1.sex = '男'
    self.age = age    # stu1.age = 28

会产生对象的名称空间,同样可以用__dict__查看

print(stu1.__dict__)
{'name': '李建刚', 'sex': '男', 'age': 28}

至此,我们造出了三个对象与一个类,对象存放各自独有的数据,类中存放对象们共有的内容

image

存的目的是为了用,那么如何访问对象或者类中存放的内容呢?

5.2 属性访问

5.2.1 类属性与对象属性

在类中定义的名字,都是类的属性,细说的话,类有两种属性:数据属性和函数属性,可以通过__dict__访问属性的值,比如Student.__dict__[‘school’],但Python提供了专门的属性访问语法

Student.school # 访问数据属性,等同于Student.__dict__['school']
'清华大学'
Student.choose # 访问函数属性,等同于Student.__dict__['choose']
<function Student.choose at 0x1018a2950>
# 除了查看属性外,我们还可以使用Student.attrib=value(修改或新增属性),用del Student.attrib删除属性。

操作对象的属性也是一样

stu1.name # 查看,等同于obj1.__dict__[‘name']
'李建刚'
stu1.course=’python’ # 新增,等同于obj1.__dict__[‘course']='python'
stu1.age=38 # 修改,等同于obj1.__dict__[‘age']=38
del obj1.course # 删除,等同于del obj1.__dict__['course']

5.2.2 属性查找顺序与绑定方法

对象的名称空间里只存放着对象独有的属性,而对象们相似的属性是存放于类中的。对象在访问属性时,会优先从对象本身的__dict__中查找,未找到,则去类的__dict__中查找

1、类中定义的变量是类的数据属性,是共享给所有对象用的,指向相同的内存地址

# id都一样
print(id(Student.school)) # 4301108704

print(id(stu1.school)) # 4301108704
print(id(stu2.school)) # 4301108704
print(id(stu3.school)) # 4301108704

2、类中定义的函数是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数

Student.choose(stu1) # 李建刚 is choosing a course
Student.choose(stu2) # 王大力 is choosing a course
Student.choose(stu3) # 牛嗷嗷 is choosing a course

但其实类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同

print(id(Student.choose)) # 4335426280

print(id(stu1.choose)) # 4300433608
print(id(stu2.choose)) # 4300433608
print(id(stu3.choose)) # 4300433608

绑定到对象的方法特殊之处在于,绑定给谁就应该由谁来调用,谁来调用,就会将’谁’本身当做第一个参数自动传入(方法__init__也是一样的道理)

stu1.choose()  # 等同于Student.choose(stu1)
stu2.choose()  # 等同于Student.choose(stu2)
stu3.choose()  # 等同于Student.choose(stu3)

绑定到不同对象的choose技能,虽然都是选课,但李建刚选的课,不会选给王大力,这正是”绑定“二字的精髓所在。

#注意:绑定到对象方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但命名为self是约定俗成的。

Python中一切皆为对象,且Python3中类与类型是一个概念,因而绑定方法我们早就接触过

#类型list就是类
print(list)
<class 'list'>

#实例化的到3个对象l1,l2,l3
l1=list([1,2,3])
l2=list(['a','b','c'])
l3=list(['x','y'])

#三个对象都有绑定方法append,是相同的功能,但内存地址不同
print(l1.append)
<built-in method append of list object at 0x10b482b48>
print(l2.append)
<built-in method append of list object at 0x10b482b88>
print(l3.append)
<built-in method append of list object at 0x10b482bc8>

#操作绑定方法l1.append(4),就是在往l1添加4,绝对不会将4添加到l2或l3
l1.append(4) #等同于list.append(l1,4)
print(l1)
[1,2,3,4]
print(l2)
['a','b','c']
print(l3)
['x','y']

5.3.3 小结

在上述介绍类与对象的使用过程中,我们更多的是站在底层原理的角度去介绍类与对象之间的关联关系,如果只是站在使用的角度,我们无需考虑语法“对象.属性"中”属性“到底源自于哪里,只需要知道是通过对象获取到的就可以了,所以说,对象是一个高度整合的产物,有了对象,我们只需要使用”对象.xxx“的语法就可以得到跟这个对象相关的所有数据与功能,十分方便且解耦合程度极高。