Python面向对象之面向对象编程

发布时间 2024-01-04 14:50:47作者: Fredette

【一】什么是面向过程

【1】面向过程介绍

  • 面向过程,核心在于 “过程” 二字
  • 过程的终极奥义就是将程序 “流程化”
  • 过程是 “流水线” ,用来分步骤解决问题的
  • 过程指的是解决问题的步骤,即先干什么再干什么......
  • 面向过程的设计就好比精心设计好一条流水线,是一种机械式的思维方式。

【2】面向过程的优点

  • 复杂度的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单)

【3】面向过程的缺点

  • 一套流水线或者流程就是用来解决一个问题,生产汽水的流水线无法生产汽车,即便是能,也得是大改,改一个组件,牵一发而动全身。

【二】什么是面向对象

【1】面向对象介绍

  • 面向过程,核心在于“对象”二字
  • 对象的终极奥义就是将程序 “整合”
  • 对象就是 “容器” ,用来盛放数据与功能

【2】面向对象的优缺点

(1)面向对象的优点

  • 解决了程序的扩展性。
  • 对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。

(2)面向对象的缺点

  • 编程的复杂度远高于面向过程,不了解面向对象而立即上手基于它设计程序,极容易出现过度设计的问题。
    • 一些扩展性要求低的场景使用面向对象会徒增编程难度,比如管理linux系统的shell脚本就不适合用面向对象去设计,面向过程反而更加适合。
  • 无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法准确地预测最终结果。
    • 于是我们经常看到对战类游戏,新增一个游戏人物,在对战的过程中极容易出现阴霸的技能,一刀砍死3个人,这种情况是无法准确预知的,只有对象之间交互才能准确地知道最终的结果。

【3】什么是程序

  • 程序 = 数据 + 功能
  • 编写程序的本质就是定义出一系列的数据,然后定义出一系列的功能来对数据进行操作。

【三】类与对象

【1】什么是类

类(Class):

类是一种抽象数据类型,是对现实世界中一类事物的抽象描述。它定义了该类事物的属性(成员变量/属性)和行为(方法/函数)。类可以看作是一种模板或蓝图,用于创建具体的对象。在类的定义中,可以包含数据成员和成员函数,用于描述类的状态和行为。

pythonCopy code
class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        print(f"{self.name} is barking.")

【2】什么是对象

对象(Object):

对象是类的实例,是具体存在的个体。在程序运行时,可以通过实例化类来创建对象。对象包含类定义的属性和行为,可以看作是类的具体实现。一个类可以有多个对象,每个对象的属性值可以不同。

pythonCopy code# 创建两个 Dog 对象
dog1 = Dog(name="Buddy", age=3)
dog2 = Dog(name="Max", age=2)

# 调用对象的方法
dog1.bark()  # 输出: Buddy is barking.
dog2.bark()  # 输出: Max is barking.

在上面的例子中,dog1dog2Dog 类的两个对象,它们具有相同的属性和方法,但属性的值可以不同。

总体而言,类是一种抽象的概念,而对象是这个概念的实际实例。通过定义类和创建对象,我们可以更好地组织和管理代码,提高代码的可维护性和可重用性。

【四】面相对象编程

【1】引入

  • 先定义一个类
  • 构建一个学校:先有对象,后有类

(1)对象

对象1:
    梦梦
    特征:
        学校=梦想学城
        姓名=梦梦
        性别=男
        年龄=18
     权力:
        读书
        写作
        跑步
对象2:
    梦梦
    特征:
    	学校=梦想学城
        姓名=萌萌
        性别=女
        年龄=30
     权力:
    	读书
        写作
        跑步
对象3:
	梦梦
    特征:
        学校=梦想学城
        姓名=朦朦
        性别=女
        年龄=20
    权力:
        读书
        写作
        跑步      

(2)类

构建的学校的类:
	相似的特征:
    	学校=梦想学城
    相似的技能:
    	读书
        写作
        跑步

(3)学生类

  • 我们可以总结出一个学生类,用来存放学生相同的数据和功能
# 学生类
	相同的特征:
    	学校=梦想学城
    相同的功能:
    	读书
        写作
        跑步

【2】类的实现和使用

  • 基于上述分析的结果,我们接下来需要做的就是在程序中定义出类,然后调用类产生对象
# 在程序中,我们要先声明一个类,在类中创建一个个对象
'''
PS
	1. 在程序中特征用变量标识,技能用函数标识
  	2. 因而类中最常见的无非是:变量和函数的定义
'''
# 在程序中定义一个类
# 类的命名应该使用“驼峰体”
class ZhangsanStudent(object):
    school = '梦想学城'

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")
        
1.查看类的名称空间
print(ZhangsanStudent.__dict__)
# {'__module__': '__main__', 'school': '梦想学城', 'read_books': <function ZhangsanStudent.read_books at 0x0000022EB2CCB7F0>, 'write_nodes': <function ZhangsanStudent.write_nodes at 0x0000022EB2FB5BD0>, 'running': <function ZhangsanStudent.running at 0x0000022EB2FB5510>, '__dict__': <attribute '__dict__' of 'ZhangsanStudent' objects>, '__weakref__': <attribute '__weakref__' of 'ZhangsanStudent' objects>, '__doc__': None}

注意:

- 类中可以有任意python代码,这些代码在类定义阶段便会执行
- 因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过`DreamStudent.__dict__`查看
- 对于经典类来说我们可以通过该字典操作类名称空间的名字(新式类有限制),但python为我们提供专门的.语法
- 点是访问属性的语法,类中定义的名字,都是类的属性


2.实例化类(调用类的过程称为将类实例化,拿到的返回值就是程序中的对象,或称为一个实例)
stu1 = ZhangsanStudent() # 每实例化一次Student类就得到一个学生对象
stu2 = ZhangsanStudent()
stu3 = ZhangsanStudent()
# 如此stu1、stu2、stu3全都一样了(只有类中共有的内容,而没有各自独有的数据)

3.初始化类
想在实例化的过程中就为三位学生定制各自独有的数据:
姓名,性别,年龄,需要我们在类内部新增一个__init__方法,如下
class ZhangsanStudent(object):
    school = '梦想学城'

    def __init__(self,name,sex,age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choose")

# 然后我们重新实例出三位学生
a1 = ZhangsanStudent("梦梦","男",18)
a2 = ZhangsanStudent("萌萌","女",30)
s3 = ZhangsanStudent("朦朦","男",20)
	单拿stu1的产生过程来分析

    调用类会先产生一个空对象stu1
    然后将stu1连同调用类时括号内的参数一起传ZhangStudent.__init__(stu1,’梦梦’,’男’,18)
    
class ZhangsanStudent(object):
    school = '梦想学城'

    def __init__(self,name,sex,age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choose")

a1 = ZhangsanStudent("梦梦","男",18)
a2 = ZhangsanStudent("萌萌","女",30)
s3 = ZhangsanStudent("朦朦","男",20)

stu1 = ZhangsanStudent('梦梦', '男', 18)
print(stu1.__dict__)
# {'name': '梦梦', 'sex': '男', 'age': 18}
至此,我们造出了三个对象与一个类,对象存放各自独有的数据,类中存放对象们共有的内容

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

【3】属性访问

(1)类属性

  • 在类中定义的名字,都是类的属性,细说的话,类有两种属性:

  • 数据属性和函数属性

  • 可以通过__dict__访问属性的值,

class ZhangStudent(object):
    school = '梦想学城'

    def __init__(self,name,sex,age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choosing")

print(ZhangStudent.__dict__)
# {'__module__': '__main__', 'school': '梦想学城', '__init__': <function ZhangStudent.__init__ at 0x0000019F5E0AB7F0>, 'read_books': <function ZhangStudent.read_books at 0x0000019F5E325BD0>, 'write_nodes': <function ZhangStudent.write_nodes at 0x0000019F5E325510>, 'running': <function ZhangStudent.running at 0x0000019F5E325C60>, 'choose': <function ZhangStudent.choose at 0x0000019F5E325CF0>, '__dict__': <attribute '__dict__' of 'ZhangStudent' objects>, '__weakref__': <attribute '__weakref__' of 'ZhangStudent' objects>, '__doc__': None}

print(ZhangStudent.school)
# 梦想学城

print(ZhangStudent.choose)
# <function ZhangStudent.choose at 0x0000019F5E325CF0>

(2)对象属性

class ZhangStudent(object):
    school = '梦想学城'

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choosing")


stu1 = ZhangStudent('梦梦', '男', 18)

print(stu1.__dict__)
# {'name': '梦梦', 'sex': '男', 'age': 18}

print(stu1.name)
# 梦梦

stu1.course = "Python"
print(stu1.course)
# Python

stu1.age = 22
print(stu1.age)
# 22

del stu1.course
print(stu1.course)
# AttributeError: 'ZhangStudent' object has no attribute 'course'

【4】属性查找

  • 对象的名称空间里只存放着对象独有的属性,而对象们相似的属性是存放于类中的。
  • 对象在访问属性时,会优先从对象本身的__dict__中查找,未找到,则去类的__dict__中查找
(1)类中定义的变量是类的数据属性,是共享给所有对象用的,指向相同的内存地址
class ZhangStudent(object):
    school = '梦想学城'

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choosing")

stundent_one = ZhangStudent("zhang", "男", 18)
stundent_two = ZhangStudent("Hope", "女", 20)
student_three = ZhangStudent("Happy", "男", 22)

print(id(ZhangStudent.school))
# 2051292978352

print(id(stundent_one.school))
# 2051292978352

print(id(stundent_two.school))
# 2051292978352

print(id(student_three.school))
# 2051292978352

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

class ZhangStudent(object):
    school = '梦想学城'

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choosing")

stundent_one = ZhangStudent("zhang", "男", 18)
stundent_two = ZhangStudent("Hope", "女", 20)
student_three = ZhangStudent("Happy", "男", 22)

ZhangStudent.choose(stundent_one)
ZhangStudent.choose(stundent_two)
ZhangStudent.choose(student_three)
# choosing
# choosing
# choosing
  • 但其实类中定义的函数主要是给对象使用的,而且是绑定给对象的
  • 虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
class ZhangStudent(object):
    school = '梦想学城'

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choosing")

stundent_one = ZhangStudent("zhang", "男", 18)
stundent_two = ZhangStudent("Hope", "女", 20)
student_three = ZhangStudent("Happy", "男", 22)

print(id(ZhangStudent.choose))
# 1489948925168
print(id(stundent_one.choose))
# 1489945975360
print(id(stundent_two.choose))
# 1489945975360
print(id(student_three.choose))
# 1489945975360
  • 绑定到对象的方法特殊之处在于,绑定给谁就应该由谁来调用,谁来调用,就会将’谁’本身当做第一个参数自动传入(方法__init__也是一样的道理)
class ZhangStudent(object):
    school = '梦想学城'

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

    def read_books(self):
        print("books")

    def write_nodes(self):
        print("nodes")

    def running(self):
        print("running")

    def choose(self):
        print("choosing")

stundent_one = ZhangStudent("zhang", "男", 18)
stundent_two = ZhangStudent("Hope", "女", 20)
stundent_three = ZhangStudent("Happy", "男", 22)

stundent_one.choose() 
# choosing
stundent_two.choose()
# choosing
stundent_three.choose()  
# choosing
  • 绑定到不同对象的choose技能

【5】list方法回顾

  • Python中一切皆为对象,且Python3中类与类型是一个概念,因而绑定方法我们早就接触过
# 类型list就是类
print(type(list))
# <class 'type'>

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

# 三个对象都有绑定方法append,是相同的功能,但内存地址不同
print(list_one.append)
# <built-in method append of list object at 0x0000022B219CDA80>
print(list_two.append)
# <built-in method append of list object at 0x0000022B21A0B880>
print(list_three.append)
# <built-in method append of list object at 0x0000022B21A16FC0>

# 操作绑定方法l1.append(4)
# 就是在往l1添加4,绝对不会将4添加到l2或l3

# 等同于list.append(list_one,4)
list_one.append(4)
print(list_one)
# [1, 2, 3, 4]

print(list_two)
# ['a', 'b', 'c']

print(list_three)
# ['x', 'y']

【五】魔法方法(init)

【1】方式一:为对象初始化自己独有的特征

class People:
    country = 'China'
    x = 1

    def run(self):
        print('------>',self)
        
# 实例化出三个空对象
obj1 = People()
obj2 = People()
obj3 = People()

# 为对象定制自己独有的特征
obj1.name = "zhangsan"
obj1.age = 18
obj1.sex = "male"

obj2.name = "lisi"
obj2.age = 30
obj2.sex = 'female'

obj3.name = 'Happy'
obj3.age = 38
obj3.sex = 'female'

print(obj1.__dict__)
# {'name': 'zhangsan', 'age': 18, 'sex': 'male'}

print(obj2.__dict__)
# {'name': 'lisi', 'age': 30, 'sex': 'female'}

print(obj3.__dict__)
# {'name': 'Happy', 'age': 38, 'sex': 'female'}

print(People.__dict__)
# {'__module__': '__main__', 'country': 'China', 'x': 1, 'run': <function People.run at 0x000001EB2FA7B7F0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}

【2】方式二:为对象初始化自己独有的特征

class People:
    country = 'China'
    x = 1

    def run(self):
        print('------>',self)
        
# 实例化出三个空对象
obj1 = People()
obj2 = People()
obj3 = People()

# 为对象定制自己独有的特征
def chu_shi_hua(obj,x,y,z):
    obj.name = x
    obj.age = y
    obj.sex = z

chu_shi_hua(obj1,'zhang',18,'male')
chu_shi_hua(obj2,'Hope',30,'female')
chu_shi_hua(obj3,'Happy',30,'female')

【3】方式三:为对象初始化自己独有的特征

class People:
    country = 'China'
    x = 1

    def chu_shi_hua(obj,x,y,z):
        obj.name = x
        obj.age = y
        obj.sex = z

    def run(self):
        print('---->',self)


obj1 = People()
print(People.chu_shi_hua)
People.chu_shi_hua(obj1,'zhang',18,'male')

obj2 = People()
People.chu_shi_hua(obj2,'hope',30,'female')

obj3 = People()
People.chu_shi_hua(obj3, 'Happy', 38, 'female')

【4】方式四:为对象初始化自己独有的特征

class People:
    country = 'China'
    x = 1

    def __init__(obj, x, y, z):
        obj.name = x
        obj.age = y
        obj.sex = z

    def run(self):
        print('---->', self)


obj1 = People('zhang', 18, 'male')

obj2 = People('hope', 30, 'female')

obj3 = People('Happy', 38, 'female')
  • 1. 站的角度不同,定义出的类是截然不同的
  • 2. 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类......
  • 3. 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类

【五】类的特殊属性

  • 类名.__name__ :类的名字(字符串)
  • 类名.__doc__:类的文档字符串
  • 类名.__base__:类的第一个父类(在讲继承时会讲)
  • 类名.__bases__:类所有父类构成的元组(在讲继承时会讲)
  • 类名.__dict__:类的字典属性
  • 类名.__module__:类定义所在的模块
  • 类名.__class__:实例对应的类(仅新式类中)
class Animal(object):
    ...


class People(Animal):
    '''这是一个人类的注释'''

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print(f"{self.name} can run!")


# 类名.__name__:类的名字(字符串)
print(People.__name__)
# People

# 类名.__doc__:类的文档字符串
print(People.__doc__)
# 这是一个人类的注释

# 类名.__base__:类的第一个父类(在讲继承时会讲)
print(People.__base__)
# <class '__main__.Animal'>

# 类名.__bases__:类所有父类构成的元组(在讲继承时会讲)
print(People.__bases__)
# (<class '__main__.Animal'>,)

# 类名.__dict__:类的字典属性
print(People.__dict__)
# {'__module__': '__main__', '__doc__': '这是一个人类的注释', '__init__': <function People.__init__ at 0x00000131B53C35B0>, 'run': <function People.run at 0x00000131B5631990>}

# 类名.__module__:类定义所在的模块
print(People.__module__)
# __main__

# 类名.__class__:实例对应的类(仅新式类中)
print(People.__class__)
# <class 'type'>

【六】代码分析

【1】数据与功能分离

  • 在没有学习类这个概念时,数据与功能是分离的
def exc1(host, port, db, charset):
    conn = connect(host, port, db, charset)
    conn.execute(sql)
    return xxx


def exc2(host, port, db, charset, proc_name):
    conn = connect(host, port, db, charset)
    conn.call_proc(sql)
    return xxx


# 每次调用都需要重复传入一堆参数
exc1('127.0.0.1', 3306, 'db1', 'utf8', 'select * from tb1;')
exc2('127.0.0.1', 3306, 'db1', 'utf8', '存储过程的名字')

【2】全局声明

  • 我们能想到的解决方法是,把这些变量都定义成全局变量
HOST = '127.0.0.1'
PORT = 3306
DB = 'db1'
CHARSET = 'utf8'


def exc1(host, port, db, charset):
    conn = connect(host, port, db, charset)
    conn.execute(sql)
    return xxx


def exc2(host, port, db, charset, proc_name):
    conn = connect(host, port, db, charset)
    conn.call_proc(sql)
    return xxx


# 每次调用都需要重复传入一堆参数
exc1(HOST, PORT, DB, CHARSET, 'select * from tb1;')
exc2(HOST, PORT, DB, CHARSET, '存储过程的名字')

【3】优化

  • 全局声明的解决方法也是有问题的,按照全局声明的思路,我们将会定义一大堆全局变量,这些全局变量并没有做任何区分,即能够被所有功能使用
  • 然而事实上只有HOST,PORT,DB,CHARSET是给exc1和exc2这两个功能用的。
  • 言外之意:我们必须找出一种能够将数据与操作数据的方法组合到一起的解决方法,这就是我们说的类了
# 改进
class MySQLHandler:
    def __init__(self, host, port, db, charset='utf8'):
        self.host = host
        self.port = port
        self.db = db
        self.charset = charset
        self.conn = connect(self.host, self.port, self.db, self.charset)

    def exc1(self, sql):
        return self.conn.execute(sql)

    def exc2(self, sql):
        return self.conn.call_proc(sql)


obj = MySQLHandler('127.0.0.1', 3306, 'db1')
obj.exc1('select * from tb1;')
obj.exc2('存储过程的名字')