一木.溪桥学Python-12:面向对象编程、私有属性、实例属性、类属性、封装、继承、单继承、super()

本文介绍了Python面向对象编程的关键概念,包括私有属性、实例属性、类属性、封装、继承和单继承,以及super()函数的使用。通过实例解释了如何定义和访问私有属性,探讨了实例属性与类属性的选择,并详细讲解了类的成员、方法类型如实例方法、类方法和静态方法。最后,文章提到了继承的概念以及super()在调用父类方法中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一木.溪桥 在Logic Education跟Amy学Python

逻辑教育 :https://logicedu.ke.qq.com

12期:Python基础课

一木.溪桥学Python-12:面向对象编程、私有属性、实例属性、类属性、封装、继承、单继承 、super()

日期:2021年1月11日


学习目标:

  • 私有属性
  • 类属性
  • 封装
  • 继承
  • 单继承
  • super()

学习内容:

私有属性

私有属性与私有方法介绍
  • 在实际开发中,对象的某些属性或者方法,只希望在对象的内部使用,这时,我
    们就可以使用私有属性和私有方法。
私有属性
  • 私有属性: 就是对象不希望公开的属性。

  • 定义方式: 在属性名前面增加两个下划线(例如:__name)

  • eg. 练习

    定义类为:"人"类
    创建对象:rose
    初始化对象属性:name 与 age
    要求:age 不能在类的外部访问

class People:
    def __init__(self, name, age):
        self.name = name
        # self.age = age
        self.__age = age

    def print_info(self):            # 1.私有属性在类的内部的方法当中是可以访问的
        print(self.__age)

    def get_age(self):               # 2.在类的内部将私有属性返回出来,进行访问
        return self.__age

    def set_age(self, new_age):      # 3.在类的外部对私有属性进行修改
        self.__age = new_age
        print(f"new age:{self.__age}")


zs = People("zs", 19)
print(zs.name)          # zs
# print(zs.age) # 在类的外部访问私有属性报错:AttributeError: 'People' object has no attribute 'age'

# 如何才能在类的外部访问私有属性?

zs.print_info()       # 1.私有属性在类的内部的方法当中是可以访问的  19
print(zs.get_age())   # 2.在类的内部将私有属性返回出来,进行访问    19
zs.set_age(50)        # 3.在类的外部对私有属性进行修改              new age:50

zs.print_info()         # 50
print(zs.get_age())     # 50
zs.set_age(30)          # new age:30

zs.print_info()         # 30
print(zs.get_age())     # 30
zs.set_age(20)          # new age:20
  • 需求:一定要在类的外部访问到对象的私有属性,如何实现?
  • 方法:
    • 可以通过类的内部的方法访问私有属性
    • 通过类的内部方法将私有属性返回出去
    • 在类的外部调用该方法并且接收
私有方法
  • 私有方法: 就是对象不希望公开的方法

  • 定义方式: 在方法名前面增加两个下划线(例如:__test)

  • 注意:

    在 python 中,并没有真正意义上的私有,只有伪私有。当我们在访问时,以对象 ._ 类名 __私有属性即可访问,私有方法同理。但是不建议使用。

class Demo:
    def test1(self):
        print("--1--")

    def __test2(self):
        print("--2--")

    def test3(self):
        return self.__test2()


d = Demo()

d.test1()          # --1--

# d.__test2()     # 私有方法没有办法直接在类的外部进行访问,报错:AttributeError: 'Demo' object has no attribute '__test2'

d.test3()         # 通过类的内部的方法去进行调用      # --2--

# d._Demo__test2()   # 注意:可以直接通过 对象._类名__私有方法名() 去访问私有方法,但是不建议使用

类的成员

在这里插入图片描述

实例属性
  • 实例属性属于对象,只能通过对象访问
  • 定义:self. 属性名称
  • 访问:self. 属性名称 或 对象 . 属性名称
eg.练习:
  • 定义一个省份类,打印输出班级每位同学所在的国家与省份。


class Province:
    def __init__(self, name):
        # 实例属性 self.属性名
        self.country = "中国"
        self.name = name

    def print_info(self):
        print(self.country, self.name)              # self就是对象本身 访问
        # print(clara.country, clara.name)          # 通过每个对象 访问 可以 但是不建议使用
        # print(Province.country, Province.name)    # 报错


clara = Province("湖南")
jx = Province("广州")
clara.print_info()
jx.print_info()

Run:
中国 湖南
中国 广州

静态属性和实例属性的选择

在实际开发中,如果属性的值是固定的,不随对象的改变而改变。那就使用
静态属性(类属性),这样有利于节省内存的消耗。而如果属性的值随对象的
改变而改变,就使用实例属性。

class Province:
    country = "中国"      # 类属性(静态属性)

    def __init__(self, name):
        # 实例属性 self.属性名
        self.name = name

    def print_info(self):
        print(self.country, self.name)          # self就是对象本身 访问 类属性
        # print(clara.country, clara.name)      # 通过每个对象 访问 可以 但是不建议使用
        print(Province.country)                 # 类属性,是可以直接通过类名来访问的


clara = Province("湖南")
jx = Province("广州")
clara.print_info()
jx.print_info()

Run:
中国 湖南
中国
中国 广州
中国

类属性 ( 静态属性 )

类属性属于类,保存在类中。在创建时,仅创建一份,并且所有对象都共享
静态属性。执行时可以是类访问也可以是对象访问。
定义: 直接在类中定义
访问: 类名 . 属性名称 或 self. 属性名称 或 对象 . 属性名称

实例方法

普通方法保存在类中,在实例化对象后,一般通过对象调用。第一个参数必
须为系统自建参数,默认为 self,代指对象本身。
注意 :
当需要在方法中使用实例属性时,通常使用实例方法
self 仅仅是变量名。使用其它也可,但最好不要修改

类方法: @classmethod

类方法通过在方法上面添加 @classmethod 装饰器,保存在类中;
注意 :
类方法不需要传入self。但有一个系统自建参数为cls,cls代指类本身;
类方法一般通过类调用,也可通过对象调用

class Demo:
    def __init__(self):
        self.name = "hello"

    # 实例方法:通常用于需要使用实例属性
    def test_one(self):
        print(self)            # <__main__.Demo object at 0x000002AE16120080>
        print(self.name)       # hello

    @classmethod
    def cls_md(cls, age):      # 自建参数不再是self, 而是cls,代指当前类
        print(cls)             # <class '__main__.Demo'>
        print(age)             # 89

    @classmethod
    def stat_md(cls):
        print("我是静态方法")     # 我是静态方法


d = Demo()
d.test_one()
d.cls_md("89")
d.stat_md()
静态方法: @staticmethod

静态方法通过在方法上面添加 @staticmethod 装饰器定义,保存在类中;
注意 :
静态方法不需要传入self参数,即使传入了self参数也并不像普通方法那样代指对象本身,仅仅是一个普通的形参。
静态方法的调用虽然可以通过对象调用,但一般由类调用。
静态方法的参数并不代表对象这一特点,使静态方法在类中使用起来更像一个单纯的函数。完全可以将静态方法放在类的外部,当成函数使用。但是放在类中,便于使用与维护

  • eg1. # 获取当前的时间
import time


# 获取当前的时间
def show_time():
    print(time.localtime())     # 返回的是当前时间,是元组格式:
    # time.struct_time(tm_year=2021, tm_mon=1, tm_mday=13, tm_hour=14, tm_min=6, tm_sec=38, tm_wday=2, tm_yday=13, tm_isdst=0)
    # 进行时间格式化
    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))     # 2021-01-13 14:06:38
    print(time.strftime("%A %a %b %B %C %c", time.localtime()))     # Wednesday Wed Jan January 20 Wed Jan 13 14:06:38 2021
    print(time.strftime("%I %p", time.localtime()))                 # 02 PM


show_time()
  • eg.2 @staticmethod 便于使用与维护
import time


class TimeTest:

    @staticmethod
    def show_time():
        # print(time.localtime())     # 返回当前时间,是元组格式,需要进行时间格式化
        print(time.strftime("%Y-%m-%d", time.localtime()))      # 2021-01-13


t = TimeTest()
t.show_time()

封装

封装介绍

封装是面向对象编程的一大特点,将属性和方法放到类的内部,通过对象访问属性或者方法,隐藏功能的实现细节,也可以设置访问权限。

在这里插入图片描述

  • eg.1:
class Student:
    addr = "123"                # 类属性(静态属性)

    def __init__(self, name, age):
        self.name = name        # 将属性封装到类的内部
        self.age = age

    def print_info(self):
        print(self.name, self.age)

        # print(self.addr)     # 123
        # self.addr = "222"   # 不能通过实例去修改类属性
        # print(self.addr)    # 222
        # print(Student.addr) # 123


# 在同一个类创建多个对象之间,属性是互不干扰的
xn = Student("寻你", 19)
jx = Student("九夏", 29)
xn.print_info()         # 寻你 19
jx.print_info()         # 九夏 29

继承 ( 重点 )

  • Python 同样支持类的继承,如果一种语言不支持继承,类就没有什么意义。

  • 继承是一种创建新类的方式,如果子类需要复用父类的属性或者方法时,就可以使用
    继承。当然,子类也可以提供自己的属性和方法。

  • 注意:在 python 中,新建的类可以继承一个或多个父类
    继承作用:避免重复造轮子,减少代码的冗余

在这里插入图片描述

新式类与经典类
  • 在 Python2 当中类分为新式类和经典类,如果有继承父类 object 则是新式类,否则为经典类。
  • 在 Python3 当中,全部都是新式类,默认继承 object。

在这里插入图片描述

  • 练习:验证 Python3 中,全部都是新式类
    实现思路:比较继承与无继承两个空类的成员是否一致
    == 拓展方法:对象 .__dir__() 查看对象的属性与方法==
# Tips:在python3 中默认都是新式类


class Demo:     # 此格式,在python2 中,叫经典类
    pass


class Demo2(object):        # 此格式,继承了object,在 python2 中,叫新式类
    pass


d1 = Demo()
d2 = Demo2()
print(len(d1.__dir__()))         # 对象 .\_\_dir\_\_() 查看对象的属性,返回的是列表。用len()测其长度: 26
print(len(d2.__dir__()))         # 对象 .\_\_dir\_\_() 查看对象的属性,返回的是列表。用len()测其长度: 26
								 # 说明了:在python3 中以上两格式都是新式类。
单继承
  • 子类继承父类,则可以直接享受父类中已经封装好的方法
  • 当对象调用方法时,查找顺序先从自身类找,如果自身没找到,则去父类找,父类
    无,再到父类的父类找,直到object类,若还无,则报错。这也称为深度优先机制。

在这里插入图片描述

在这里插入图片描述

  • 需要注意的是,当子类与父类拥有同名称的方法时,子类对象调用该方法优先执行自身的方法。那么实际上就是子类的方法覆盖父类的方法,也称为重写。
  • 但是实际的开发中,遵循开放封闭原则。我们并不会完全的重写父类的方法,而是希望同时实现父类的功能。这时,我们就需要调用父类的方法了,可以通过 super()函数实现。
super()
  • super(type[, object-or-type]) 函数是用于调用父类(超类)的一个方法
    type --> 类
    object-or-type --> 对象或类,一般是 self

  • eg.练习: 继以上练习实现,在Son的sleep方法当中,调用父类的sleep方法。


class GrandFather(object):
    def sleep(self):
        print("GrandFather sleep 10")


class Father(GrandFather):
    def eat(self):
        print("Father eat")

    def drink(self):
        print("Father drink")


class Son(Father):
    def study_python(self):
        print("Son study python")

    def sleep(self):
        print("Son sleep 8")    # 当子类有与父类名字相同的方法时,就意味着是重写了父类的方法。
        # 在执行自己的方法的时候,仍然继承了父类的方法
        # super(Son, self).sleep()      # super() , Son的对象去调用父类的sleep的方法
        # supper().sleep()              # 参数不传也可以
        GrandFather.sleep(self)         # 通过 类名.方法名(self),调用父类的sleep的方法


s = Son()

s.study_python()            # Son study python

s.eat()                     # Father eat
                            # 通过子类的对象可以使用父类的方法,子类没有则去父类找
s.sleep()                   # Son sleep 8      GrandFather sleep 10
                            # 子类没有,则去父类找,父类没有,则继续去父类的父类找...

  • Run:
    Son study python
    Father eat
    Son sleep 8
    GrandFather sleep 10

“”"
1.单继承:深度优先
2.重写: 防止执行父类当中的方法
3.self永远指的是执行该方法的调用者
4.super(当前类,self).父类中的方法(arg)
“”"

  • Tips:
  • __init__方法也会继承,同实例方法一致
  • 私有属性以及私有方法没有被继承
"""
思考:__init__ 方法是否会被继承?
会被继承,并且同实例方法一致;深度优先。
"""

class Father(object):
    def __init__(self):
        self.name = "amy"
        self.age = 18

    def test(self):
        print("test")

    def __test2(self):
        print("__test2")


class Son(Father):
    def get_test(self):
        print(self.name)
        self.test()

        # 注意:私有属性不会被继承
        # print(self.__age)

        # 注意:私有方法也不会被继承
        # self.__test2()


s = Son()
print(s.name)
# print(s.__age)
s.get_test()
  • Run:
    amy
    amy
    test

作业:

在这里插入图片描述


作业答案:

题1:

"""
玩个特别无聊的游戏 , 猜数字。
玩家输入一个数字与
计算机随机生成的数字作对比
当两个值相等时 , 则说明用户猜对了
注意 : 外部不能获取到计算机随机生成的值
"""

import random

num_max = int(input("请输入一个最大数字:"))

ran_num = random.randint(0, num_max)

flag = 1

while flag:

    ipt_num = int(input(f"请猜一个0~{num_max}之间的数:"))

    # print(ran_num)

    if ipt_num == ran_num:
        print(f"恭喜,猜对了!就是{ran_num}!")
        break

    elif ipt_num < ran_num:
        print("猜小了!再猜!")
    else:
        print("猜大了!再猜!")

  • Run:
    请输入一个最大数字:20
    请猜一个0~20之间的数:10
    猜小了!再猜!
    请猜一个0~20之间的数:15
    猜小了!再猜!
    请猜一个0~20之间的数:30
    猜大了!再猜!
    请猜一个0~20之间的数:18
    猜大了!再猜!
    请猜一个0~20之间的数:17
    恭喜,猜对了!就是17!
Amy的答案:

"""
玩个特别无聊的游戏,猜数字。
玩家输入一个数字与
计算机随机生成的数字作对比
当两个值相等时,则说明用户猜对了
注意:外部不能获取到计算机随机生成的值
"""

import random


class GuessNun(object):
    def __init__(self):
        self.__rand_num = random.randint(1, 10)
        print(f"random_number:{self.__rand_num}")

    def guess_it(self):
        ipt_num = int(input("请输入1-10的数:"))

        while True:
            if self.__rand_num  == ipt_num:
                print("猜对了!")
                break
            else:
                ipt_num = int(input("猜的不对奥,请重新输入:"))


b = GuessNun()
b.guess_it()

Run:
random_number:4
请输入1-10的数:46
猜的不对奥,请重新输入:2
猜的不对奥,请重新输入:6
猜的不对奥,请重新输入:4
猜对了!

题2:没做对,等答案哈!


import time


class JianBing(object):

    def __init__(self):
        self.time0 = 0

    def s_unripe(self):
        print("煎饼是生的!")

    def s_underdone(self):
        print("煎饼是半生不熟的!")

    def s_ripe(self):
        print("煎饼熟了!")

    def s_scorch(self):
        print("煎饼焦了!")

    def zuoliao(self):
        print("煎饼要加大葱、 大蒜 、 烤肠吗?")


# def calcu_time()
#     pass
#     start = time.time()
#     end = time.time()
#     res = end - start
#     return res

J = JianBing()

print("客人:师傅来个煎饼!", "师傅:好的马上做!", sep='\n')

# use_time = calcu_time()

use_time = 6
if 0 < use_time <= 3:
    J.s_unripe()
elif 3 <use_time <= 5:
    J.s_underdone()
elif 5 <use_time <= 8:
    J.s_ripe()
    J.zuoliao()
else:
    J.s_scorch()

  • Run:
Amy的答案:
"""
创建一个煎饼类 调用烹饪时长的方法累计煎饼状态
如果煎的时间在0-3之间则状态为生的
如果煎的时间在3-5之间则状态为半生不熟的
如果煎的时间在5-8之间则状态为全熟的
当时间超过8分钟状态焦了
并且还可以给煎饼添加作料
比如大葱(hhh),大蒜(hhh)?,烤肠等等
"""


class PanCake(object):
    def __init__(self):
        self.cake_status = "生的"
        self.cake_level = 0
        self.condiments = []

    def __str__(self):
        str1 = "+"
        return f"煎饼的状态:{self.cake_status}, 煎饼的时长:{self.cake_level}, 添加的作料:{ str1.join(self.condiments)}"

    def cook(self, cooked_time):
        self.cake_level += cooked_time
        if self.cake_level >= 0 and self.cake_level < 3:
            self.cake_status = "生的"
        elif self.cake_level >= 3 and self.cake_level < 5:
            self.cake_status = "半生不熟"
        elif self.cake_level >=5 and self.cake_level < 8:
            self.cake_status = "全熟"
        elif self.cake_level >= 8:
            self.cake_status = "焦了"

    def add_condiments(self, food):
        self.condiments.append(food)


pc = PanCake()
pc.cook(1)
pc.cook(5)
pc.add_condiments("热狗")
pc.add_condiments("鸡蛋")
pc.add_condiments("洋葱")
print(pc)

Run:
煎饼的状态:全熟, 煎饼的时长:6, 添加的作料:热狗+鸡蛋+洋葱


End ! Best wishes for you!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值