python 第十二章 面向对象

系列文章目录

第一章 初识python
第二章 变量
第三章 基础语句
第四章 字符串str
第五章 列表list []
第六章 元组tuple ( )
第七章 字典dict {}
第八章 集合set {}
第九章 常用操作
第十章 函数
第十一章 文件操作



理解面向对象
面向对象是一种抽象化的编程思想,很多编程语言中都有的一种思想。

例如: 洗衣服

思考: 几种途径可以完成洗衣服?
答: 手洗和机洗
手洗: 找盆- 放水加洗衣粉 - 浸泡 - 搓洗 - 拧干水 - 倒水 - 漂洗N次 - 晾晒
机洗: 打开洗衣机 - 放衣服 - 加洗衣粉 - 按下开始按钮 - 晾晒

思考: 对比两种洗衣服途径,同学们发现了什么?
答: 机洗更简单
思考: 机洗,只需要找到一台洗衣机,加入简单操作就可以完成洗衣服的工作,而不需要关心洗衣机内部发生了什么事情。

总结: 面向对象就是将编程当成是一个事物,对外界来说,事物是直接使用的,不用去管他内部的情况。而编程就是设置事物能够做什么事。

类和对象
思考: 洗衣机洗衣服描述过程中,洗衣机其实就是一个事物,即对象,洗衣机对象哪来的呢?答: 洗衣机是由工厂工人制作出来。
思考: 工厂工人怎么制作出的洗衣机?
答:工人根据设计师设计的功能图纸制作洗衣机。
总结: 图纸 --> 洗衣机 --> 洗衣服。

在面向对象编程过程中,有两个重要组成部分: 对象
类和对象的关系·用类去创建一个对象

理解类和对象

类是对一系列具有相同特征行为的事物的统称,是一个抽象的概念,不是真实存在的事物。

  • 特征即是属性
  • 行为即是方法

类比如是制造洗衣机时要用到的图纸,也就是说类是用来创建对象
在这里插入图片描述

对象
对象是类创建出来的真实存在的事物,例如:洗衣机。

注意:开发中,先有类,再有对象。

在这里插入图片描述

12.1面向对象实现方法

定义类

Python2中类分为: 经典类新式类

  • 语法
    class 类名():
      代码
      ......

注意: 类名要满足标识符命名规则,同时遵循大驼峰命名习惯

  • 体验
class Washer():
	def wash(self):
		print('洗衣服')

拓展: python2.x解释器中,默认我们的类是按照经典类去处理去解释的。python3.x解释器中,我们的类默认是按照新式类去处理去解释的。

经典类(或旧式类)

不由任意内置类型派生出的类,称之为经典类。

class 类名:
   代码
   ......

新式类

class 类名(object):
   代码

class 类名(要继承的父类类名):
   代码

默认状态下如果一个类不去继承自己书写的类,这个时候它默认继承的是所有类的顶级类(所有类的父类,也叫基类)object。

创建对象

对象又名实例。

  • 语法
    对象名 = 类名()

  • 体验

# 创建对象
haier1 = Washer()
#  <__main__.Washer object at 0x000001E01066CF50>
print(haier1)
# haier对象调用实例方法
haier1.wash()

调用类里的方法

  • 语法
    对象名.对象方法名()

在这里插入图片描述

self

self指的是调用该函数的对象。

class Washer():
    def wash(self):
        print('洗衣服')
        print(self)  # <__main__.Washer object at 0x0000021AF9DDCF50>


haier = Washer()
print(haier)  # <__main__.Washer object at 0x0000021AF9DDCF50>
haier.wash()

在这里插入图片描述

由于打印对象和打印self得到的内存地址相同,所以self指的是调用该函数的对象

一个类创建多个对象

多个对象调用函数的时候,self地址不相同。
在这里插入图片描述

12.2添加和获取对象属性

属性即是特征,比如: 洗衣机的宽度、高度、重量…
对象属性既可以在类外面添加和获取,也能在类里面添加和获取。

类外面添加对象属性

对象名.属性名 = 值

class Washer():
    def wash(self):
        print('洗衣服')


haier = Washer()

# 添加对象属性
haier.height = 850
haier.width = 600
print(f"haier洗衣机的高度度是{haier.height}")
print(f"haier洗衣机的宽度是{haier.width}")

在这里插入图片描述

类外面获取对象属性

对象名.属性名

类里面获取对象属性

self.属性名

class Washer():
    def info(self):
        print(f"洗衣机的高度度是{self.height}")
        print(f"洗衣机的宽度是{self.width}")


haier = Washer()
haier.height = 850
haier.width = 600

haier.info()

12.3 魔法方法

在Python中,__xx__()的函数叫做魔法方法,指的是具有特殊功能的函数。

__init__()

体验__init__()

思考: 洗衣机的高度宽度是与生俱来的属性,可不可以在生产过程中就赋予这些属性呢?
答:理应如此。

__init__()方法的作用: 初始化对象

class Washer():
    # 定义__init__,添加实例属性
    def __init__(self):
        # 添加实例属性
        self.width = 600
        self.height = 850

    def info(self):
        print(f"洗衣机的高度度是{self.height}")
        print(f"洗衣机的宽度是{self.width}")


haier1 = Washer()
haier1.info()

注意:

  • init()方法,在创建一个对象时默认被调用,不需要手动调用。
  • init(self)中的self参数,不需要开发者传递,python解释器会自动把当前的对象引用传递过去。

带参数的__init__()

思考:一个类可以创建多个对象,如何对不同的对象设置不同的初始化属性呢?
答: 传参数。

class Washer():
    # 定义__init__,添加实例属性
    def __init__(self, width, height):
        # 添加实例属性
        self.width = width
        self.height = height

    def info(self):
        print(f"洗衣机的高度度是{self.height}, 洗衣机的宽度是{self.width}")


haier1 = Washer(100, 150)
haier1.info()

haier2 = Washer(200, 280)
haier2.info()

__str__()

当使用print输出对象的时候,默认打印对象的内存地址。如果类定义了__str__方法,那么就会打印从在这个方法中 return 的数据。

class Washer():
    def __init__(self):
        self.width = 200

    def __str__(self):
        return f"洗衣机的宽度是{self.width}"


haier1 = Washer()
print(haier1)  # 洗衣机的宽度是200

__del__()

当删除对象时,python解释器也会默认调用__del__()方法

class Washer():
    def __del__(self):
        print(f"{self}对象已被删除")


h = Washer()
del h  #  这行可省略
# <__main__.Washer object at 0x00000200670CCF90>对象已被删除

最后一行代码可以省略,当执行完所有的代码,会自动的结束运行,结束之后内存删除所有的变量、函数、类,这些都会被内存自动删除释放,__del__()自然而然就会被调用到。

12.4 继承

继承的概念
生活中的继承,一般指的是子女继承父辈的财产。

默认状态下如果一个类不去继承自己书写的类,这个时候它默认继承的是所有类的顶级类object。

Python面向对象的继承指的是多个类之间的所属关系,即子类默认继承父类的所有属性和方法,具体如下:

class Father(object):
    def __init__(self):
        self.num = 1

    def info_print(self):
        print(self.num)


class Son(Father):
    pass


result = Son()
result.info_print()  # 1

为了语法不报错,写一个pass。 pass 是空语句,是为了保持程序结构的完整性。pass 不做任何事情,一般用做占位语句。

在Python中,所有类默认继承objiect类,object类是顶级类或基类; 其他子类叫做派生类。

单继承

一个父亲类继承给了一个儿子类,这种单一的继承关系,叫做单继承。

class Father(object):
    def __init__(self):
        self.recipe = "[意式烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜')


class Son(Father):
    pass


s1 = Son()
print(s1.recipe)

多继承

所谓多继承意思就是一个类同时继承了多个父类。

class Father(object):
    def __init__(self):
        self.recipe = "[意式烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜')


class School(object):
    def __init__(self):
        self.recipe = "[学校学的烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜2')


class Son(Father, School):
    pass


s1 = Son()
print(s1.recipe)
s1.cook()

在这里插入图片描述
在这里插入图片描述

注意: 当一个类有多个父类的时候.,默认使用第一个父类的同名属性和方法。

子类重写父类同名方法和属性

class Father(object):
    def __init__(self):
        self.recipe = "[意式烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜')


class School(object):
    def __init__(self):
        self.recipe = "[学校学的烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜2')


class Son(School, Father):
    def __init__(self):
        self.recipe = "[独创的烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜3')


s1 = Son()
print(s1.recipe)
s1.cook()

在这里插入图片描述

如果子类和父类拥有同名属性和方法,子类创建对象调用属性和方法的时候,调用到的是子类里面的同名属性和方法。

扩展:mro顺序

print(Son.__mro__)

打印即可得到继承层级顺序。
在这里插入图片描述

子类调用父类的同名方法和属性

加入子类调用父类的同名方法之后就要必须把自己的__init__()初始化也要执行一下

class Son(School, Father):
    def __init__(self):
        self.recipe = "[独创的烤地瓜配方]"

    def cook(self):
        # 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
        self.__init__()
        print(f'运用{self.recipe}制作烤地瓜3')

    # 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
	# 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
    def father_cook(self):
        Father.__init__(self) # 再次调用初始化的原因: 这里想要调用父类的同名方法和属性,属性在init初始化位置,所以需要调用初始化
        Father.cook(self)

    def school_cook(self):
        School.__init__(self)
        School.cook(self)


s1 = Son()
s1.cook()
s1.father_cook()

1.如果调用父类方法不穿传self,会报错
在这里插入图片描述
2.缺少调用父类的初始化

在这里插入图片描述

多层继承

儿子类继承父类,孙子类继承儿子类,仍然可以使用父类的所有方法。

class Father(object):
    def __init__(self):
        self.recipe = "[意式烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜')


class School(object):
    def __init__(self):
        self.recipe = "[学校学的烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜2')


class Son(School, Father):
    def __init__(self):
        self.recipe = "[独创的烤地瓜配方]"

    def cook(self):
        # 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
        self.__init__()
        print(f'运用{self.recipe}制作烤地瓜3')

    # 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
    # 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
    def father_cook(self):
        Father.__init__(self)  # 再次调用初始化的原因: 这里想要调用父类的同名方法和属性,属性在init初始化位置,所以需要调用初始化
        Father.cook(self)

    def school_cook(self):
        School.__init__(self)
        School.cook(self)


class Grandson(Son):
    pass


sunzi = Grandson()
sunzi.cook()
sunzi.father_cook()
sunzi.school_cook()

12.5 super()调用父类方法

修变一下,改成单继承,现在Father类继承School类,Son类继承Father类,多层继承,都是定义了同名的属性与方法。

如果我想在Son类一次性调用到Father和School的方法,怎么做到?

1.方法一: 原始写法,代码冗余; 父类类名如果变化,这里代码需要频繁修改
在这里插入图片描述
2.方法二:super() 调用父类的方法
①super(当前类名,self).函数()
在这里插入图片描述
super().函数()

class School(object):
    def __init__(self):
        self.recipe = "[学校学的烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜2')


class Father(School):
    def __init__(self):
        self.recipe = "[意式烤地瓜配方]"

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜')
        # # 方法2.1
        # super(Father, self).__init__()
        # super(Father, self).cook()

        # 方法2.2 无参数super()
        super().__init__()
        super().cook()


class Son(Father):
    def __init__(self):
        self.recipe = "[独创的烤地瓜配方]"

    def cook(self):
        # 如果是先调用了父类的属性和方法,父类属性会覆盖子类属性,故在调用属性前,先调用自己子类的初始化
        self.__init__()
        print(f'运用{self.recipe}制作烤地瓜3')

    # 子类调用父类的同名方法和属性:把父类的同名属性和方法再次封装
    # 调用父类方法,但是为保证调用到的也是父类的属性,必须在调用方法前调用父类的初始化
    def father_cook(self):
        Father.__init__(self)  # 再次调用初始化的原因: 这里想要调用父类的同名方法和属性,属性在init初始化位置,所以需要调用初始化
        Father.cook(self)

    def school_cook(self):
        School.__init__(self)
        School.cook(self)

    def make_old_cook(self):
        # # 方法一
        # Father.__init__(self)
        # Father.cook(self)
        # School.__init__(self)
        # School.cook(self)

        # # 方法2.1
        # super(Son, self).__init__()
        # super(Son, self).cook()

        # 方法2.2 无参数super()
        super().__init__()
        super().cook()


s1 = Son()
s1.make_old_cook()

注意:使用super()可以自动查找父类。调用顺序遵循__mro__类属性的顺序。比较适合单继承使用。

12.6 私有权限

定义私有属性和方法

前面写的属性和方法,前面没有__的,叫公有权限。

在Python中,可以为实例属性和方法设置私有权限,对象的某些属性只想在对象的内部被使用,但不想在类的外部被访问到这些属性

私有属性可以被继承但无法直接访问。

设置私有权限的方法: 在属性名和方法名 前面加上两个下划线__。

语法:
class 类名():
   # 私有属性
   __属性名 = 值

   # 私有方法
   def __函数名(self):
   代码

注意:私有属性和私有方法只能在类里面访问和修改,该类自己的实例对象也不能调用。

class Father(object):
    def __init__(self):
        self.recipe = "[意式烤地瓜配方]"
        self.__money = 100000000  # 定义私有属性

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜1')

    def __print_info(self):  # 定义私有方法
        print(self.__money)


class Son(Father):
    pass


s1 = Son()
# print(s1.__money) # 报错
s1.__print_info()  # 报错

在这里插入图片描述
在这里插入图片描述

获取和修改私有属性值

在Python中,一般定义函数名 get_xx 用来获取私有属性,定义 set_xx 用来修改私有属性值,工作习惯。

class Father(object):
    def __init__(self):
        self.recipe = "[意式烤地瓜配方]"
        self.__money = 100000000  # 定义私有属性

    def cook(self):
        print(f'运用{self.recipe}制作烤地瓜1')

    def __print_info(self):  # 定义私有方法
        print(self.__money)

    def get_money(self):
        return self.__money

    def set_money(self):
        self.__money = 666


class Son(Father):
    pass


fa = Father()
# print(fa.__money)# 报错
print(fa.get_money())  # 100000000
fa.set_money()
print(fa.get_money())  # 666

print("+++++++++++++++++++++++")

s1 = Son()
# print(s1.__money)  # 报错
# s1.__print_info()  # 报错
print(s1.get_money())  # 100000000
s1.set_money()
print(s1.get_money()) # 666

12.7 面向对象三大特征

  • 封装
    • 将属性和方法书写到类的里面的操作即为封装
    • 封装可以为属性和方法添加私有权限
  • 继承
    • 子类默认继承父类的所有属性和方法
    • 子类可以重写父类属性和方法
  • 多态
    • 传入不同的对象,产生不同的结果

多态

多态指的是一类事物有多种形态,(一个抽象类有多个子类,因而多态的概念依赖于继承)。

python的多态不是必须依赖继承,最好依赖于继承。

  • 定义: 多态是一种使用对象的方式,子类重写父类方法,调用不同子类对象的相同父类方法,可以产生不同的执行结果
  • 好处: 调用灵活,有了多态,更容易编写出通用的代码,做出通用的编程,以适应需求的不断变化!
  • 步骤
    • 定义父类,并提供公共方法
    • 定义子类,并重写父类方法
    • 传递子类对象给调用者,可以看到不同子类执行效果不同
class Dog(object):
    def work(self):  # 父类提供统一的方法,哪怕是空方法
        # print("指哪打哪...")
        pass


# 萨摩耶
class SamoyedDog(Dog):  # 继承Dog类
    def work(self):  # 子类重写父类同名方法
        print("卖萌撒娇")


# 搜救犬
class RescueDog(Dog):
    def work(self):
        print("寻找被困或失踪的人员")


# 缉毒犬
class DrugDog(Dog):
    def work(self):
        print("追查毒品")


class Person(object):
    def work_with_dog(self, dog):  # 传入不同的对象,执行不同的代码,即不同的work函数
        dog.work()


sd = SamoyedDog()
rd = RescueDog()
dd = DrugDog()

p = Person()

p.work_with_dog(sd)  # 卖萌撒娇
p.work_with_dog(rd)  # 寻找被困或失踪的人员
p.work_with_dog(dd)  # 追查毒品

12.8类属性和实例属性

类属性是定义在类级别上的属性,它是类的所有实例共享的。当在类定义中定义一个变量时,它就成为了类属性。

实例属性是每个类实例特有的属性,每个实例都有自己的属性。

类属性

设置和访问类属性

  • 类属性就是 类对象 所拥有的属性,它被 该类的所有实例对象共有
  • 类属性可以使用 类对象实例对象 访问。
class Dog(object):
    tooth = 10


kajimi = Dog()
meiqiu = Dog()

print(Dog.tooth)  # 10
print(kajimi.tooth)  # 10
print(meiqiu.tooth)  # 10

类属性的优点

  • 记录的某项数据 始终保持一致时,则定义类属性
  • 实例属性 要求 每个对象 为其 单独开辟一份内存空间 来记录数据,而 类属性 为全类所共有,仅占用一份内存,更加节省内存空间。

修改类属性

类属性只能通过类对象修改,不能通过实例对象修改,如果通过实例对象修改类属性,表示的是创建了个实例属性。

class Dog(object):
    tooth = 10


kajimi = Dog()
meiqiu = Dog()

# 修改类属性,类.类属性 = 值
Dog.tooth = 14
print(Dog.tooth)  # 14
print(kajimi.tooth)  # 14
print(meiqiu.tooth)  # 14

# 不能通过实例对象修改,如果这样操作,实则是创建了一个实例属性
kajimi.tooth = 20
print(Dog.tooth)  # 14
print(kajimi.tooth)  # 20
print(meiqiu.tooth)  # 14

12.9类方法和静态方法

类方法

类方法的特点
需要用装饰器 @classmethod 来标识其为类方法,对于类方法,第一个参数必须是类对象,一般以cls 作为第一个参数。

@classmethod
def xx(cls):
   代码

类方法使用场景

  • 当方法中需要使用类对象(如访问私有类属性等)时,定义类方法
  • 类方法一般和类属性配合使用
# 1. 定义类: 私有类属性,类方法获取这个私有类属性
class Dog(object):
    __tooth = 12

    # 定义类方法
    @classmethod
    def get_tooth(cls):
        return cls.__tooth


# 2.创建对象,调用类方法
meiqiu = Dog()
result = meiqiu.get_tooth()
print(result)  # 12

静态方法

静态方法的特点

  • 需要通过装饰器 @staticmethod 来进行修饰,静态方法既不需要传递类对象也不需要传递实例对象(形参没有self/cls)
  • 静态方法也能够通过实例对象类对象去访问

@staticmethod
def xx():
   代码

静态方法使用场景

  • 当方法中既不需要使用实例对象(如实例对象、实例属性)也不需要使用类对象(如类属性、类方法、创建实例等时,定义静态方法
  • 取消不需要的参数传递,有利于减少不必要的内存占用和性能消耗
class Dog(object):
    @staticmethod
    def print_info():
        print("这是一个狗狗类")


meiqiu = Dog()
# 静态方法既可以使用对象访问又可以使用类访问
meiqiu.print_info()
Dog.print_info()

扩展:__dict__

收集类对象或实例对象的属性和方法以及对应的值,从而返回一个字典。

class A(object):
    x = 0

    def __init__(self):
        self.y = 1


aa = A()
# 返回类内部所有属性和方法对应的字典
print(A.__dict__)
# 返回实例属性和值组成的字典
print(aa.__dict__)

在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值