面向对象

发布时间 2024-01-04 20:12:38作者: 苏苏!!

面向对象

(一)什么是面向过程

(1)面向过程介绍

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

(2)面向过程的优点

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

(3)面向过程的缺点

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

(二)什么是面向对象

(1)面向对象介绍

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

(2)面向对象的优点

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

(3)面向对象的缺点

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

(3)什么是程序

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

(4)小结

  • 在了解了对象的基本概念之后,理解面向对象的编程方式就相对简单很多了
  • 面向对象编程就是要造出一个个的对象,把原本分散开的相关数据与功能整合到一个个的对象里
  • 这么做既方便使用,也可以提高程序的解耦合程度,进而提升了程序的可扩展性(需要强调的是,软件质量属性包含很多方面,面向对象解决的仅仅只是扩展性问题)

(三)类与对象

(1)引入

  • 类即类别/种类,是面向对象分析和设计的基石,如果多个对象有相似的数据与功能,那么该多个对象就属于同一种类。
  • 有了类的好处是:
    • 我们可以把同一类对象相同的数据与功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需存自己独有的数据即可,极大地节省了空间。
  • 所以,如果说对象是用来存放数据与功能的容器,那么类则是用来存放多个对象相同的数据与功能的容器。

![image-20240102150937821](D:\old boy\typora\笔记\我的python28笔记\20\image-20240102150937821.png)

  • 综上所述,虽然我们是先介绍对象后介绍类,但是需要强调的是:
    • 在程序中,必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)。
    • 产生对象的类与对象之间存在关联,这种关联指的是:对象可以访问到类中共有的数据与功能
    • 所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制
  • 面向对象编程最终的核心仍然是去使用对象。

(2)什么是类

  • 类即类别、种类,是面向对象设计最重要的概念,对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体
  • 所以,先有鸡和先有蛋的问题就出来了
    • 先有的一个个具体存在的对象(比如一个具体存在的人)
    • 还是先有的人类这个概念,这个问题需要分两种情况去看

(1)现实中

  • 在现实世界中:先有对象,再有类
  • 世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,如人类、动物类、植物类等概念
  • 也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在

(2)程序中

  • 在程序中:务必保证先定义类,后产生对象
  • 这与函数的使用是类似的,先定义函数,后调用函数,类也是一样的,在程序中需要先定义类,后调用类
  • 不一样的是,调用函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象

(四)类的定义

(1)类的定义

  • 语法
'''
# 定义类
class(关键字) 类名():
    # 定义参数
    # 定义函数
# 调用类 ---> 得有一个具体的对象才能调用类
'''

"""定义学生类"""
class student():
    school='大学!'
    def read(self):
        print("读书")
    def write(self):
        print("写作")
    def running(self):
        print("跑步")

(2)类的实现

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

方式一:

class student():
    school = '大学!'

    def read(self):
        print("读书")

    def write(self):
        print("写作")

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

# 调用:先实例化类得到一个对象
s1 = student()
#dir查看对象具有的功能
print(dir(s1))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'read', 'running', 'school', 'write']
print(s1)  # <class '__main__.student'>
print(s1.school)  # 大学!
print(s1.__dict__)#{}
# {'__module__': '__main__', 'school': '大学!', 'read': <function student.read at 0x0000020C64672320>, 'write': <function student.write at 0x0000020C64672950>, 'running': <function student.running at 0x0000020C646739A0>, '__dict__': <attribute '__dict__' of 'student' objects>, '__weakref__': <attribute '__weakref__' of 'student' objects>, '__doc__': None}
s1.__dict__['name'] = 'syh'
s1.__dict__['age'] = 23
print(s1.__dict__)#{'name': 'syh', 'age': 23}
print(s1.name)#syh

方式二:

class student():
    school = '大学!'

    def read(self):
        print("读书")

    def write(self):
        print("写作")

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

def init(ogj,name,age,gender):
    #3.向对象中修改对象独有的特性
    ogj.__dict__['name']=name
    ogj.__dict__['age']=age
    ogj.__dict__['gender']=gender

#1.调用先实例化得到一个对象
s1=student()
print(s1)#<__main__.student object at 0x0000012FEEAF7FD0>
#2.调用初始化对象的方法,得到独有的对象属性
init(ogj=s1,name='syh',age=23,gender='男')
print(s1.__dict__)#{'name': 'syh', 'age': 23, 'gender': '男'}

方式三:

class student():
    school = '大学!'
    def read(self):
        print("读书")
    def write(self):
        print("写作")
    def running(self):
        print("跑步")
    def init(obj,name,age,gender):
        #3.向对象中修改对象独有的特性
        obj.__dict__['name']=name
        obj.__dict__['age']=age
        obj.__dict__['gender']=gender

#1.调用先实例化得到一个对象
s1=student()
print(s1.__dict__)
#{}
print(dir(s1))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'init', 'read', 'running', 'school', 'write']
#2.调用初始化对象方法,得到独有的对象属性
#对象调用类的方法时,会将对象本身作为参数self传进去
s1.init(name='syh',age=23,gender='female')
print(s1.__dict__)
#{'name': 'syh', 'age': 23, 'gender': 'female'}

方式四:

  • 类的魔法方法
    • 想在实例化的过程中就为学生定制各自独有的数据:
      • 姓名,性别,年龄,需要我们在类内部新增一个__init__方法,如下
class student():
    school = '大学!'

    #魔法方法-----init初始化方法
    def __init__(self,name,age,gender):
        #3.向对象中修改对象独有的特性
        self.name=name
        self.age=age
        self.gender=gender

    def read(self,height):
        print(f"{height}读书")
    def write(self):
        print("写作")
    def running(self):
        print("跑步")

#1.调用先实例化得到一个对象
s1=student(name='syh',age=23,gender="男")
print(s1.__dict__)
#{'name': 'syh', 'age': 23, 'gender': '男'}
print(dir(s1))
#['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'gender', 'init', 'name', 'read', 'running', 'school', 'write']

s1.read(height=180)
#180

(3)注册登录练习

class ATM():
    # 存储注册信息!
    user_data_dict = {}

    def __init__(self):
        # 用来声明用户登录成功!
        self.username = ''
        self.balance = None

    def register(self):
        print("注册!")
        username = input("请输入用户名:").strip()
        password = input("请输入名密码:").strip()
        self.user_data_dict['username'] = username
        self.user_data_dict['password'] = password
        print(self.user_data_dict)
        self.login()

    def login(self):
        print("登录!")
        username = input("请输入用户名:").strip()
        password = input("请输入名密码:").strip()
        if username not in self.user_data_dict['username']:
            self.register()
        if password == self.user_data_dict['password']:
            print("登陆成功!")
            self.username = username
            self.balance = 1000
            self.withdraw()
        else:
            print("密码错误!")
            self.login()

    def withdraw(self):
        print("取款!")
        if self.username == '':
            self.login()
        get_balance = input("请输入取款金额:").strip()
        get_balance = int(get_balance)
        if get_balance > self.balance:
            print("余额不足!")
            self.withdraw()
        else:
            self.balance -= get_balance
            print(f"取款{get_balance},剩余{self.balance}")

    def main(self):
        print("主函数")
        if self.user_data_dict == {}:
            self.register()
        else:
            self.login()


atm = ATM()
atm.main()# ATM -- 类里面有两个功能 登录注册
# ATM : 注册 , 登录 , 登陆成功以后初始化你的信息
# 取款

class ATM():
    # 存储注册信息!
    user_data_dict = {}

    def __init__(self):
        # 用来声明用户登录成功!
        self.username = ''
        self.balance = None

    def register(self):
        print("注册!")
        username = input("请输入用户名:").strip()
        password = input("请输入名密码:").strip()
        self.user_data_dict['username'] = username
        self.user_data_dict['password'] = password
        print(self.user_data_dict)
        self.login()

    def login(self):
        print("登录!")
        username = input("请输入用户名:").strip()
        password = input("请输入名密码:").strip()
        if username not in self.user_data_dict['username']:
            self.register()
        if password == self.user_data_dict['password']:
            print("登陆成功!")
            self.username = username
            self.balance = 1000
            self.withdraw()
        else:
            print("密码错误!")
            self.login()

    def withdraw(self):
        print("取款!")
        if self.username == '':
            return False
        get_balance = input("请输入取款金额:").strip()
        get_balance = int(get_balance)
        if get_balance > self.balance:
            print("余额不足!")
            self.withdraw()
        else:
            self.balance -= get_balance
            print(f"取款{get_balance},剩余{self.balance}")

    def main(self):
        print("主函数")
        if self.user_data_dict == {}:
            self.register()
        else:
            self.login()


atm = ATM()
atm.main()

(五)类属性与对象属性

  • 类属性
    • 数据属性
    • 函数属性

(1)类属性

"""类属性"""
# 类属性分为两种:一种是数据属性,另一种是函数属性
class Student():
    # 这个school就是数据属性,对象可以任意调用
    school = '清华大学'

    def __init__(self,name,age,gender):
        self.name=name
        self.age=age
        self.gender=gender
    # 这个read函数就是函数属性
    def read(self,name):
        print(f"{name}正在读书")

# 实例化类得到类对象
s1=Student(name='syh',age=23,gender='female')
# 访问数据属性的方法
# 一种通过__dict__的字典取键
print(Student.__dict__)
# {'__module__': '__main__', 'school': '清华大学', 'init': <function Student.init at 0x000001721D602320>, 'read': <function Student.read at 0x000001721D602950>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}
print(Student.__dict__['school'])#清华大学
# 第二种是通过类名.属性 获取到数据属性的值
print(Student.school)#清华大学

#对象可以通过__dict__获取到类属性吗?
print(s1.__dict__)#{'name': 'syh', 'age': 23, 'gender': 'female'}
# 不可以:
print(s1.__dict__['school'])#KeyError: 'school'
print(s1.school)#清华大学


#访问函数属性
s1=Student(name='syh',age=23,gender='female')
# 类调用对象函数属性 因为有一个位置参数叫 self ,类调用需要传入 self 实例化出来的对象
Student.__dict__['read'](s1,'syh')#syh正在读书
# 对象调用函数属性,这里的位置参数会自动将调用该方法的对象传入,所以不用传self参数
s1.read(name='syh')#syh正在读书

(2)对象属性的查找顺序

"""对象属性的查找顺序"""
# 对象的属性查找顺序
# 【1】首先从自己本身开始找 从 stu.__dict__ 开始找
# 【2】找不到再去实例化他的类中找 从 Student.__dict__ 开始找
# 【3】别的方法没有初始化过这个属性,并且这个类没有继承其他的类
# 【4】找不到就报错了

# 总结 : obj(对象) ---> class(类) ---> gender_class(父类) ---> 找不到直接报错

(3)类的特殊方法

"""类的特殊方法"""

from new_class import Cat
class Cat():
    ...


# 【一】定义一个类
class Animal():
    ...


class BlackCat(Cat):
    ...


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

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

    def run(self):
        '''
        这是一个注释
        :return:
        '''
        print(f"{self.name} can run!")


# def login_auth(func):
#     def inner():
#         # 查看函数的名字
#         print(func.__name__) # index
#         return func()
#     return inner

# @login_auth
# def index():
#     ...
# index()

# 【1】实例化类得到对象
person = People(name='dream', age=18)


# 【二】类的名字
def check_class_name(class_name):
    print(class_name.__name__)
check_class_name(class_name=People)
check_class_name(class_name=Animal)
# 【三】查看类的文档字符串,查看类中的多行注释内容 ('''''' / """""")
def check_class_name(class_name):
    print(class_name.__doc__)
check_class_name(class_name=People)  # 这是一个人类的注释
check_class_name(class_name=Animal)  # None
# 【四】类的字典属性
def check_class_name(class_name):
    print(class_name.__dict__)
check_class_name(class_name=People)  # 这是一个人类的注释
check_class_name(class_name=Animal)  # None
# 【五】查看类定义所在的模块
def check_class_name(class_name):
    print(class_name.__module__)
check_class_name(class_name=People)  # __main__
check_class_name(class_name=Animal)  # __main__
check_class_name(class_name=Cat)  # new_class
# 【六】查看当前实例所对应的类
def check_class_name(obj_name):
    print(obj_name.__class__)
check_class_name(obj_name=People(name='dream', age=18))  # __main__
check_class_name(obj_name=Animal())  # __main__
check_class_name(obj_name=Cat())  # new_class

(六)绑定方法与非绑定方法

  • 绑定方法:绑定给某个目标(对象或类)的方法-----动态方法
  • 非绑定方法:没有绑定给某个目标的方法-----静态方法

(一)绑定方法

  • 绑定方法:绑定给某个目标(对象或类)的方法-----动态方法
  • 绑定给谁,谁来调用就自动将它本身当作第一个参数传入

(1)绑定到类的方法

  • 用classmethod装饰器装饰的方法。
  • 为类量身定制
  • 类.boud_method(),自动将类当作第一个参数传入
  • (其实对象也可调用,但仍将类当作第一个参数传入)

(2)绑定到对象的方法

  • 没有被任何装饰器装饰的方法
  • 为对象量身定制
  • 对象.boud_method(),自动将对象当作第一个参数传入
  • (属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说)
"""绑定方法与非绑定方法"""
# 1.绑定给对象
class Student():  # 类的两种属性:数据属性和函数属性
    # 数据属性
    school = '清华大学'
    def read(self, name):
        print(f"{name}正在读书")
# 对象调用对象的绑定方法
# 实例化得到对象
s1 = Student()
# 在这里对象调用绑定给对象的绑定方法时,会自动将当前对象作为第一个参数传入
s1.read(name='syh')  # syh正在读书
#类调用对象的绑定方法
#类调用对象的绑定方法时,就需要将对象作为参数传进去
Student.read(s1,'syh')#syh正在读书
"""总结"""
# 对象调用对象绑定的方法时,不需要额外的传入参数,之间调用执行
# 类调用对象绑定的方法时,第一个位置参数必须将实例化得到的对象传入,然后再加入其他可能传入的参数


# 2.绑定给类
class Student():  # 类的两种属性:数据属性和函数属性
    # 数据属性
    school = '清华大学'
    def read(self, name):
        print(f"{name}正在读书")
    @classmethod
    def write(cls,name):
        print(f"{name}正在写作")
#对象调用类绑定的方法
#实例化得到对象
s1=Student()
#对象调用类绑定的方法时,这里会自动检索到当前实例化得到的对象的类,然后将类作为cls传入
s1.write(name='syh')#syh正在写作

#类调用类绑定的方法
#类调用类绑定的对象时,这里将调用此方法的类作为cls的参数传入
Student.write(name='syh')#syh正在写作
"""总结"""
# 对象调用类的绑定方法时,会自动检索当前实例化得到的对象的类,将类当多cls的参数传入,不需要传入额外的值
# 类调用类的绑定方法时,会将调用次方法的类当作cls传入,不需要传入额外的值

(二)非绑定方法

  • 非绑定方法:没有绑定给某个目标的方法-----静态方法
  • 用staticmethod装饰器装饰的方法
  • 不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。
  • 就是一个普通工具而已
"""非绑定方法"""
class Student():  # 类的两种属性:数据属性和函数属性
    # 数据属性
    school = '清华大学'
    def read(self, name):
        print(f"{name}正在读书")
    @classmethod # 装饰器语法糖
    def write(cls,name):
        print(f"{name}正在写作")
    @staticmethod# 非绑定方法,实质上就是一个普普通通的函数
    def jump(name):
        print(f"{name}正在跳高")
#对象调用非绑定方法
s1=Student()
#对象调用非绑定方法时,不需要传入额外的参数
s1.jump(name='syh')#syh正在跳高
#类调用非绑定方法
#类调用非绑定方法时,不需要传入额外的参数
Student.jump(name='syh')#syh正在跳高
"""总结"""
# 对象调用非绑定方法时,不需要传入额外的参数
# 类调用非绑定方法时,不需要传入额外的参数

(总结)

"""绑定方法与非绑定方法总结"""
"""概念上"""
# 绑定方法:绑定给某个目标(类或者对象)的方法
# 非绑定方法:不绑定给某个目标的方法

"""调用方式"""
# 绑定给对象的方法:对象可以任意调用,类可能会收到限制
# 绑定给类的方法,对象可能会收到限制,类也可以任意调用
# 不绑定方法:对象可以任意调用,类也可以任意调用

"""定义上看"""
class Student():
    # 1.定义给对象的方法,正常我们定义的所有方法都是绑定给对象的方法
    # 定义的时候,会自动不全self
    def read(self):
        ...
    # 2.绑定给类的方法,用装饰器装饰我们想要绑定给类的方法
    # 定义的时候,会自动不去cls
    @classmethod
    def run(cls):
        ...
    # 3.非绑定方法,既不绑定对象,也不绑定类
    # 再定义的时候,不会自动补全参数
    @staticmethod
    def write():
        ...
    
"""调用方式"""
# 1.绑定给对象的-----动态
# 对象调用
s1=Student()
s1.read()
# 类调用
Student.read(Student())
# 2.绑定给类的-----动态
# 对象调用
s1.run()
# 类调用
Student.run()
# 3.非绑定方法-----静态
# 对象调用
s1.write()
# 类调用
Student.write()