首页 > 编程语言 >Python基础学习(十一)面向对象编程(进阶)

Python基础学习(十一)面向对象编程(进阶)

时间:2024-11-02 18:16:38浏览次数:3  
标签:__ 进阶 Python self 面向对象编程 print class def name

代码获取:https://github.com/qingxuly/hsp_python_course
完结版:Python基础学习(完结版)

面向对象编程(进阶)

面向对象编程三大特征

  • 面向对象编程有三大特征:封装、继承、多态。

面向对象编程—封装

封装介绍
  • 封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部。
  • 程序只有通过被授权的操作,才能对数据进行访问。
  • 封装经典案例:电视机的操作就是典型封装。
封装的理解和好处
  • 隐藏实现细节:方法(绘制柱状图)<——调用(传入参数…)。
  • 可以对数据进行验证(比如:age: 1~120、password: 长度要求),保证安全合理。
  • 可以保护数据隐私(比如salary),要求授权才可以访问。
私有成员
  • 公共的变量和方法介绍。
    • 默认情况下,类中的变量和方法都是公有的,它们的名称前都没有下划线。
    • 公共的变量和方法,在类的外部、类的内部,都可以正常访问。
  • 如何将属性/方法进行私有化。
    • 类中的变量或方法以双下划线__开头命名,则该变量或方法为私有的,私有的变量或方法,只能在本类内部使用,类的外部无法使用。
  • 如何访问私有的属性/方法:提供公共的方法,用于对私有成员的操作。
快速入门
# 创建职员类(Clerk),属性:name, job, salary
# 1、不能随便查看职员 Clerk 的职位和工资等隐私,比如职员 ("tiger", "python 工程师", 20000)
# 2、提供公共方法,可以对职员和工资进行操作

class Clerk:
    # 公共属性
    name = None
    # 私有属性
    __job = None
    __salary = None

    # 构造方法
    def __init__(self, name, job, salary):
        self.name = name
        self.__job = job
        self.__salary = salary

    # 提供公共的方法,对私有属性操作(根据实际的业务编写即可)
    def set_job(self, job):
        self.__job = job

    def get_job(self):
        return self.__job

    # 私有方法
    def __hi(self):
        print("hi()")

    # 提供公共方法,操作私有方法
    def f1(self):
        self.__hi()


clerk = Clerk("tiger", "python 工程师", 20000)
print(clerk.name)

# print(clerk.__job)  # AttributeError: 'Clerk' object has no attribute '__ job'
print(clerk.get_job())
clerk.set_job("Java 工程师")
print(clerk.get_job())

# 私有方法不能在类的外部直接调用
# clerk.hi()  # AttributeError: 'Clerk' object has no attribute 'hi'

# 通过公共方法,调用私有方法
clerk.f1()
注意事项和细节
  • python语言的动态特性,会出现伪私有属性的情况
class Clerk:
    # 公共属性
    name = None
    # 私有属性
    __job = None
    __salary = None

    # 构造方法
    def __init__(self, name, job, salary):
        self.name = name
        self.__job = job
        self.__salary = salary

    # 提供公共的方法,对私有属性操作
    def get_job(self):
        return self.__job


clerk = Clerk("apple", "python 工程师", 20000)

# 如果这样使用,因为 python 语言的动态特性,会动态的创建属性 __job,但是这个属性和我们在类中定义的私有属性 __ job 不是同一个变量,我们在类中定义的__job 私有属性完整的名字是 _Clerk__job,而并不是
# 可以通过 debug 的“Evaluate Expression”来观察。
clerk.__job = "Go 工程师"
print(f " job = {clerk.__job}")  # job = Go 工程师

# 获取真正的私有属性__job
print(f "{clerk.get_job()}")
  • 练习
# Account 类要求具有属性:姓名(长度为 2-4 位)、余额(必须 > 20)、密码(必须是六位),如果不满足,则给出提示
# 通过 set_xx 的方法给 Account 的属性赋值
# 编写 query_info() 接收姓名和密码,如果姓名和密码正确,返回该账号信息

"" "
    思路分析:
    类名:Account
    私有属性:姓名(长度为 2-4 位)、余额(必须 > 20)、密码(必须是六位)
    构造器:无
    方法:set_xx(self, 属性名) 进行赋值,并且对各个接收到的数据进行校验
    方法:query_info(self, name, pwd) 而且需要验证,才返回响应信息
"" "


class Account:
    __name = None
    __balance = None
    __pwd = None

    def set_name(self, name):
        if 2 <= len(name) <= 4:
            self.__name = name
        else:
            print("名字的长度不在 2-4 位之间")

    def set_balance(self, balance):
        if balance > 20:
            self.__balance = balance
        else:
            print("余额(必须 > 20)")

    def set_pwd(self, pwd):
        if len(pwd) == 6:
            self.__pwd = pwd
        else:
            print("密码(必须是六位)")

    def query_info(self, name, pwd):
        if name == self.__name and pwd == self.__pwd:
            return f "账户信息:{self.__name} {self.__ balance}"
        else:
            return "请输入正确的名字和密码"


# 测试
account = Account()
account.set_name("tim")
account.set_pwd("000000")
account.set_balance(100)
print(account.query_info("tim", "000000"))

面向对象编程—继承

基本介绍
  • 继承基本介绍

    • 基础可以解决代码复用,让我们的编程更加靠近人类思维。
    • 当多个类存在相同的属性(成员变量)和方法时,可以从这些类中抽象出方法,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法。
  • 示意图

image-20240807183802865

  • 基本语法
class DerivedClassName(BaseClassName):
    <statement-1>
    ...
    <statement-N>
    
# 派生类就会自动拥有基类定义的属性和方法
# 基类习惯上也叫父类
# 派生类习惯上也叫子类
快速入门
# 编写父类
class Student:
    name = None
    age = None
    __score = None

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

    def show_info(self):
        print(f " name ={self.name}, age ={self.age}, score ={self.__score}")

    def set_score(self, score):
        self.__score = score


# 小学生类,继承 Student
class Pupil(Student):
    def testing(self):
        print("小学生在考小学数学...")


# 大学生类,继承 Student
class Graduate(Student):
    def testing(self):
        print("大学生在考高等数学...")


# 测试
student1 = Pupil("apple", 10)
student1.testing()
student1.set_score(70)
student1.show_info()

student2 = Graduate("grape", 22)
student2.testing()
student2.set_score(80)
student2.show_info()
  • 继承给编程带来的便利
    • 代码的复用性提高了。
    • 代码的扩展性和维护性提高了。
继承的注意事项和细节
  • 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法在子类不能在子类直接访问,要通过父类提供公共的方法去访问。
# 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法在子类不能在子类直接访问,要通过父类提供公共的方法去访问。
class Base:
    # 公共属性
    n1 = 100
    # 私有属性
    __n2 = 200

    def __init__(self):
        print("Base 构造方法")

    def hi(self):
        print("hi() 公共方法")

    def __hello(self):
        print("__hello() 私有方法 ")

    # 提供公共方法,访问私有属性和私有方法
    def test(self):
        print("属性:", self.n1, self.__n2)
        self.__hello()


class Sub(Base):

    def __init__(self):
        print("Sub 构造方法")

    def say_ok(self):
        print("say_ok() ", self.n1)
        self.hi()

        # print(self.__n2)  # AttributeError: 'Sub' object has no attribute '_Sub__n2'
        # self.__hello()  # AttributeError: 'Sub' object has no attribute '_Sub__n2'


# 测试
sub = Sub()
sub.say_ok()

# 调用子类继承父类的公共方法,去实现访问父类的私有成员的效果
sub.test()
  • Python语言中,object是所有其它类的基类。Ctrl + H可以查看一个类的继承关系。

  • Python支持多继承。

class A:
    n1 = 100

    def sing(self):
        print("A sing()...", self.n1)


class B:
    n2 = 200

    def dance(self):
        print("B dance()...", self.n2)


class C(A, B):
    # Python pass 是空语句,是为了保持程序结构的完整;pass 不做任何事情,一般用作站位语句。
    pass


c = C()
# 继承的属性信息
print(f "属性信息:{c.n1} {c.n2}")
# 调用继承的方法
c.dance()
c.sing()
  • 在多重继承中,如果有同名的成员,遵守从左到右的继承优先级(即:写在左边的父类优先级高,写在右边的父类优先级低)。
class A:
    n1 = 100

    def sing(self):
        print("A sing()...", self.n1)


class B:
    n2 = 200

    def dance(self):
        print("B dance()...", self.n2)

    def sing(self):
        print("B sing()...", self.n2)


class C(A, B):
    # Python pass 是空语句,是为了保持程序结构的完整;pass 不做任何事情,一般用作站位语句。
    pass


c = C()
# 继承的属性信息
print(f "属性信息:{c.n1} {c.n2}")
# 调用继承的方法
c.dance()
c.sing()  # A sing()... 100
继承的练习题
class GrandPa:
    name = "大头爷爷"
    hobby = "旅游"


class Father(GrandPa):
    name = "大头爸爸"
    age = 39


class Son(Father):
    name = "大头儿子"


son = Son()
print(f "son.name is {son.name} and son.age is {son.age} and son.hobby is {son.hobby}")
class Computer:
    cpu = None
    memory = None
    disk = None

    def __init__(self, cpu, memory, disk):
        self.cpu = cpu
        self.memory = memory
        self.disk = disk

    def get_details(self):
        return f "CPU: {self.cpu}, Memory: {self.memory}, Disk: {self.disk}"


class PC(Computer):
    brand = None

    def __init__(self, cpu, memory, disk, brand):
        # 初始化子类的属性——方式 1
        # self.cpu = cpu
        # self.memory = memory
        # self.disk = disk
        # self.brand = brand

        # 初始化子类的属性——方式 2
        # 通过 super().xx 方式可以去调用父类方法,这里通过 super().__init__(cpu, memory, disk, brand) 去调用父类的构造器
        super().__init__(cpu, memory, disk)
        self.brand = brand

    def print_info(self):
        print(f "品牌:{self.brand}\t{self.get_details()}")


pc = PC("inter", 32, 1000, "戴尔")
pc.print_info()

调用父类成员

基本介绍&实例
  • 基本介绍
    • 如果子类和父类出现同名的成员,可以通过父类名super() 访问父类的成员。
  • 基本语法
    • 访问父类成员方式1
      • 访问成员变量:父类名.成员变量
      • 访问成员方法:父类名.成员方法(self)
    • 访问父类成员方式2
      • 访问成员变量:super().成员变量
      • 访问成员方法:super().成员方法()
  • 案例演示
class A:
    n1 = 100

    def run(self):
        print("A-run()...")


class B(A):
    n1 = 200

    def run(self):
        print("B-run()...")

    # 通过父类名去访问父类的成员
    def say(self):
        print(f "父类的 n1 ={A.n1} 本类的 n1 ={self.n1}")
        # 调用父类的 run
        A.run(self)

        # 调用本类的 run()
        self.run()

    # 通过 super()方式去访问父类对象
    def hi(self):
        print(f "父类的 n1 ={super().n1}")


b = B()
b.say()
b.hi()
注意事项和使用细节
  • 子类不能直接访问父类的私有成员
class A:
    n1 = 100
    __n2 = 600

    def run(self):
        print("A-run()...")

    def __jump(self):
        print("A-jump()...")


class B(A):
    # 子类不能直接访问父类的私有成员
    def say(self):
        # print(A.__n2)  # AttributeError: type object 'A' has no attribute '_B_ _n2'. Did you mean: '_A__n2'?
        # print(super().__n2)  # AttributeError: 'super' object has no attribute '_B__n2'

        # A.__jump(self)
        # super().jump()
        print("B-say()...")


b = B()
b.say()
  • 访问不限于直接父类,而是建立从子类向上级父类的查找关系 A->B->C…
class Base:
    n3 = 800

    def fly(self):
        print("Base-fly()...")


class A(Base):
    n1 = 100
    __n2 = 600

    def run(self):
        print("A-run()...")

    def __jump(self):
        print("A-jump()...")


class B(A):
    # 访问不限于直接父类,而是建立从子类向上级父类的查找关系 B-> A-> Base...
    def say(self):
        print(Base.n3, A.n3, super().n3)

        Base.fly(self)
        A.fly(self)
        super().fly()
        self.fly()


b = B()
b.say()
  • 建议使用super()的方式,因为如果使用父类名方式,一旦父类变化,类名统一需要修改,比较麻烦。
练习题
  • 分析下面的代码,看看输出什么内容?
class A:
    n1 = 300
    n2 = 500
    n3 = 600

    def fly(self):
        print("A-fly()...")


class B(A):
    n1 = 200
    n2 = 400

    def fly(self):
        print("B-fly()...")


class C(B):
    n1 = 100

    def fly(self):
        print("C-fly()...")

    def say(self):
        print(self.n1)  # 100
        print(self.n2)  # 400
        print(self.n3)  # 600
        print(super().n1)  # 200
        print(B.n1)  # 200
        print(C.n1)  # 100


c = C()
c.say()
  • 针对上面的程序,想在C的say()中,调用C的fly()和A的fly(),应该如何调用?
self.fly()  # C-fly()...
A.fly(self)  # A-fly()...

方法重写

基本介绍&实例
  • 基本介绍
    • 重写又称覆盖(override),即子类继承父类的属性和方法后,根据业务需要,再重新定义同名的属性和方法。
  • 案例演示
image-20240822123940420
练习题
class Person:
    name = None
    age = None

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

    def say(self):
        return f "名字:{self.name} 年龄:{self.age}"


class Student(Person):
    id = None
    score = None

    def __init__(self, id, score, name, age):
        # 调用父类的构造器完成继承父类的属性和属性的初始化
        super().__init__(name, age)
        # 子类的特有的属性,我们自己完成初始化
        self.id = id
        self.score = score

    def say(self):
        return f "id:{self.id} score:{self.score} {super().say()}"


person = Person("tom", 12)
print(person.say())

student = Student("tom", 14, "tom", 18)
print(student.say())

类型注解-type hint

基本介绍
  • 为什么需要类型注解
    • 随着项目越来越大,代码也就会越来越多,在这种情况下,如通过没有类型注解,很容易不记得某一个方法的参数类型是什么。
    • 一旦传入了错误类型的参数,python是解释性语言,只有运行时候才能发现问题,这对大项目来说是一个巨大的灾难。
# 对字符串进行遍历
# a: str 给形参 a 进行类型注解,标注 a 的类型是 str。
def fun1(a: str):
    for ele in a:
        print(ele)


# Ctrl + P 提示参数时,没有类型提示
# 如果类型传错了,就会出现异常
fun1(100)  # TypeError: 'int' object is not iterable
  • 类型注解作用和说明
    • 从Python3.5开始,引入了类型注解机制,作用和说明如下:
      • 类型提示,防止运行时出现参数类型、返回值类型、变量类型不符合。
      • 作为开发文档附加说明,方便使用者调用时传入和返回类型参数。
      • 加入后并不会影响程序的运行,不会报正式的错误,只有提示。
      • Pycharm支持类型注解,参数类型错误会黄色提示。
变量的类型注释
  • 基本语法:变量: 类型

  • 基础数据类型注解

n1: int = 10  # 对n1进行类型注解,标注n1的类型为int;如果给出的类型与标注的类型类型不一致,则Pycharm会给出黄色警告
n2: float = 10.1
is_pass: bool = True
name: str = "Tom"
  • 实例对象类型注解
class Cat:
    pass


cat: Cat = Cat()  # 对cat进行类型注解,标注cat类型是Cat
  • 容器类型注解
my_list: list = [1, 2, 3]  # 对my_list进行类型注解,标注my_list类型为list。
my_tuple: tuple = ("run", "sing", "fly")
my_set: set = {1, 2, 3}
my_dict: dict = {"name": "Tom", "age": 22}
  • 容器详细类型注解
my_list1: list[int] = [1, 2, 3]  # 对my_list1进行类型注解,标注my_list1类型是list,而且该list元素是int。
# 元组类型设置详细类型注解,需要把每个元素类型都标注一下
my_tuple1: tuple[str, str, str, float] = ("run", "sing", "fly", 1.1)
my_set1: set[int] = {1, 2, 3}
# 字典类型设置详细类型注解,需要设置两个类型,即[key类型, value类型]
# 对my_dict1进行类型注解,标注my_dict1类型是dict,而且key的类型是str,values的类型是int。
my_dict1: dict[str, int] = {"score": 100, "score2": 80}
  • 在注释中使用注解
    • 基本语法:# type: 类型
# # type: float 用于标注变量n3的类型是float。
n3 = 89.9  # type: float
my_list3 = [100, 200, 300]  # type: list[int]
email = "[email protected]"  # type: str
函数(方法)的类型注解
  • 基本语法
def 函数/方法名(形参名: 类型, 形参名: 类型 ...) -> 返回值类型:
	函数/方法体
  • 实例演示
# 对字符串进行遍历
# name: str 对形参name进行类型注解:标注name类型是str;在调用方法/函数时,传入的实参类型不是一样的,则给出黄色的警告。
def fun1(name: str):
    for ele in name:
        print(ele)


fun1("hsp")


# 接收两个整数,返回整数
# a: int, b: int 对形参a、b进行类型注解,标注a、b的类型为int; -> int 对返回值进行类型注解,标注返回值的类型为int。
def fun2(a: int, b: int) -> int:
    return a + b


print(f"结果是:{fun2(1, 2)}")

类型注解是提示性的,并不是强制性的,如果你给的类型和指定/标注的类型不一致,Pycharm检测到会给出黄色警告,但是仍然可以运行。

Union类型

  • 基本介绍
    • Union类型可以定义联合类型注解。
    • 在变量、函数(方法)都可以使用Union联合类型注解。
    • 使用的时候,需要先导入Union:from typing import Union
  • 基本语法
Union[类型, 类型...]
比如:联合类型:Union[X, Y] 等价于 X|Y,意味着满足X或Y之一。
  • 实例演示
from typing import Union

# 联合类型注解, a可以是int或str
a: Union[int, str] = 100

# my_list是str类型,元素可以是int或str
my_list: list[Union[int, str]] = [1, 2, 3, "tom"]


# 函数/方法使用联合函数注解
# 接收两个数(可以是int/float),返回数(int/float)
def cal(num1: Union[int, float], num2: Union[int, float]) -> Union[int, float]:
    return num1 + num2


print(cal(1, 2.1))

面向对象编程—多态

问题引入
  1. Master类中有一个feed(喂食)方法,可以完成主人给动物喂食物的信息。

image-20241009220358366

# 使用传统的方式
class Food:
    name = None

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


class Fish(Food):
    pass


class Bone(Food):
    pass


class Animal:
    name = None

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


class Dog(Animal):
    pass


class Cat(Animal):
    pass


class Master:
    name = None

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

    # 给猫喂鱼
    def feed_cat(self, cat: Cat, fish: Fish):
        print(f"主任{self.name} 给动物{cat.name} 喂{fish.name}")

    # 给狗喂骨头
    def feed_dog(self, dog: Dog, bone: Bone):
        print(f"主人{self.name} 给动物{dog.name} 喂{bone.name}")


master = Master("老韩")
cat = Cat("小花猫")
fish = Fish("黄花鱼")
dog = Dog("大黄狗")
bone = Bone("大棒骨")

master.feed_cat(cat, fish)
master.feed_dog(dog, bone)
  1. 传统方法带来的问题:代码的复用性不高,而且不利于代码维护和功能扩展。解决方法是使用多态。
多态介绍
  1. 多态
    • 多态顾名思义即多种状态,不同的对象调用相同的方法,表现出不同的状态,称为多态。
    • 多态通常作用在继承关系上。
    • 举例说明:一个父类,具有多个子类,不同的子类对象调用相同的方法,执行的时候产生不同的状态,就是多态。
  2. 代码演示
class Animal:
    def cry(self):
        pass


class Cat(Animal):
    def cry(self):
        print("喵喵喵")


class Dog(Animal):
    def cry(self):
        print("汪汪汪")


class Pig(Animal):
    def cry(self):
        print("噜噜噜")


# 在Python面向对象编程中,子类对象可以传递给父类类型
def func(animal: Animal):
    print(f"animal类型:{type(animal)}")
    animal.cry()


cat = Cat()
dog = Dog()
pig = Pig()

func(cat)
func(dog)
func(pig)
  1. 多态的好处

    • 增加了程序的灵活性,以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal),代码说明。
    • 增加了程序的可扩展性,通过继承Animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用。
  2. Python多态特点

    • Python中函数/方法的参数是没有类型限制的,所以多态在Python中的体现并不是很严谨(与Java等强类型语言比)。
    • Python并不要求严格的继承体系,关注的不是对象的类型本身,而是它是否具有要调用的方法(行为)。
class AA:
    def hi(self):
        print("AA-hi()...")


class BB:
    def hi(self):
        print("BB-hi()...")


def test(obj):
    obj.hi()


aa = AA()
bb = BB()
test(aa)
test(bb)
二说喂动物问题
# 使用多态的方式
class Food:
    name = None

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


class Fish(Food):
    pass


class Bone(Food):
    pass


class Animal:
    name = None

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


class Dog(Animal):
    pass


class Cat(Animal):
    pass


# 扩展 Horse、Grass,体会多态的机制带来的好处
class Horse(Animal):
    pass


class Grass(Food):
    pass


class Master:
    name = None

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

    def feed(self, animal: Animal, food: Food):
        print(f"主人{self.name} 给动物{animal.name} 喂{food.name}")


master = Master("老韩")
cat = Cat("小花猫")
fish = Fish("黄花鱼")
dog = Dog("大黄狗")
bone = Bone("大棒骨")
horse = Horse("枣红马")
grass = Grass("嫩草")

master.feed(cat, fish)
master.feed(dog, bone)
master.feed(horse, grass)
isinstance函数
  1. 基本说明
    • isinstance() 用于判断对象是否为某个类或其子类的对象
    • 基本语法:isinstance(object, classinfo)
      • object:对象。
      • classinfo:可以是类名、基本类型或者由它们组成的元组。
class AA:
    pass


class BB(AA):
    pass


class CC:
    pass


obj = BB()
obj2 = AA()

print(f"obj 是不是BB的对象 {isinstance(obj, BB)}")
print(f"obj 是不是AA的对象 {isinstance(obj, AA)}")
print(f"obj 是不是CC的对象 {isinstance(obj, CC)}")

num = 9
print(f"num 是不是int:{isinstance(num, int)}")
print(f"num 是不是str:{isinstance(num, str)}")

print(f"num 是不是str/int/list:{isinstance(num, (str, int, list))}")
练习
  1. 分析代码输出结果。

当调用对象成员的时候,会和对象本身动态关联。

class A:
    i = 10

    def sum(self):
        # self是A还是B?——B
        # 当调用对象成员的时候,会和对象本身动态关联
        return self.getI() + 10

    def sum1(self):
        return self.i + 10

    def getI(self):
        return self.i


class B(A):
    i = 20

    # def sum(self):
    #     return self.i + 20

    def getI(self):
        return self.i

    # def sum1(self):
    #     return self.i + 10


b = B()
print(b.sum())  # 30
print(b.sum1())  # 30

  1. 编程题
  1. 定义员工类Employee,包含私有属性(姓名和月工资),以及计算年工资get_annual的方法。

  2. 普通员工(Worker)和经理(Manager)继承员工类,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求根据需要重写get_annual方法。

  3. 编写函数show emp annual(e:Employee),实现获取任何员工对象的年工资。

  4. 编写函数working(e:Employee),如果是普通员工,则调用work方法,如果是经理,则调用manage方法。

class Employee:
    __name = None
    __salary = None

    # 构造器
    def __init__(self, name, salary):
        self.__name = name
        self.__salary = salary

    def get_annual(self):
        return self.__salary * 12

    def set_name(self, name):
        self.__name = name

    def get_name(self):
        return self.__name

    def get_salary(self):
        return self.__salary


class Worker(Employee):
    def work(self):
        print(f"普通工人 {self.get_name()} 正在工作中")


class Manager(Employee):
    __bonus = None

    def __init__(self, name, salary, bonus):
        super().__init__(name, salary)
        self.__bonus = bonus

    def manage(self):
        print(f"经理 {self.get_name()} 正在工作中")

    def get_annual(self):
        return super().get_annual() + self.__bonus


def show_emp_annual(e: Employee):
    print(f"{e.get_name()} 年工资: {e.get_annual()}")


worker = Worker("king", 10000)
show_emp_annual(worker)

manager = Manager("tim", 20000, 100000)
show_emp_annual(manager)


def working(e: Employee):
    if isinstance(e, Worker):
        e.work()
    elif isinstance(e, Manager):
        e.manage()
    else:
        print("无法确定工作状态")


working(worker)
working(manager)

[魔术方法](3. 数据模型 — Python 3.13.0 文档)

基本介绍
  1. 什么是魔术方法
    • 在Python中,所有以双下划线__包起来的方法,统称为Magic Method(魔术方法),它是一种特殊方法,普通方法需要调用,而魔术方法不需要调用就可以自动执行。
    • 魔术方法在类或对象的某些事情发生时会自动执行,让类具有神奇的“魔力”。如果希望根据自己的程序定制特殊功能的类,那么就需要对这些方法进行重写。
    • Python中常用的运算符、for循环以及类操作都是运行在魔术方法之上的。
  2. 常见的魔术方法
序号操作
1__init__:初始化对象的成员
2__str__(self):定义对象转字符的行为:print(对象)或str(对象)
3__eq__(self, other):定义等于号的行为:x == y
4__lt__(self, otehr):定义小于号的行为:x < y
5__le__(self, otehr):定义小于等于号的行为:x ≤ y
6__ne__(self, otehr):定义不等号的行为:x != y
7__gt__(self, self):定义大于号的行为:x > y
8__ge__(self, other):定义大于等于号的行为:x ≥ y
__str__
  1. 基本介绍
    • 打印对象默认返回:类型名+对象内存地址,子类往往重写__str__,用于返回对象的属性信息。
      • <__main__.Monster object at 0x000002414EC85070>
    • 重写__str__方法,print(对象)或str(对象)时,都会自动调用该对象的__str__
  2. 应用实例
class Monster:
    name = None
    age = None
    gender = None

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

    # 请输出 Monster[name, job, sal] 对象的属性信息
    """
        1. 在默认情况下,调用的是父类。Monster的父类object的__str__
        2. 父类object的__str__ 返回的就是类型+地址
        3. 可以根据需要重写__str__
    """
    def __str__(self):
        # return super().__str__()
        return f"{self.name} {self.age} {self.gender}"


m = Monster("青牛怪", 500, "n男")
print(m)  # 默认输出类型+对象的地址
print(m, hex(id(m)))
__eq__
  1. 基本介绍

    • == 是一个比较运算符:对象之间进行比较时,比较的内存地址是否相等,即判断是不是同一个对象。
    • 重写 __eq__方法,可以用于判断对象内容/属性是否相等。
  2. 应用实例

    判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回True,反之返回False。

    Person类,属性(name, age, gender)

class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # 判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回True,反之返回False。
    def __eq__(self, other):
        # 判断other 是不是 Person
        if isinstance(other, Person):
            return (self.name == other.name and
                    self.age == other.age and
                    self.gender == other.gender)
        return False


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


# 没有重写 __eq__ 前, == 比较的是内存地址;重写 __eq__ 后, == 比较的是属性/内容
p1 = Person("smith", 20, "male")
p2 = Person("smith", 20, "male")
dog = Dog("smith", 20, "male")

print(f"p1 == p2: {p1 == p2}")
print(f"p1 == dog: {p1 == dog}")
其它几个魔术方法
  1. 其它几个魔术方法

    • __lt__(self, otehr):定义小于号的行为:x < y。
    • __le__(self, otehr):定义小于等于号的行为:x ≤ y。
    • __ne__(self, otehr):定义不等号的行为:x != y。
    • __gt__(self, self):定义大于号的行为:x > y。
    • __ge__(self, other):定义大于等于号的行为:x ≥ y。
  2. 应用实例

    判断两个Person对象的年龄进行比较大小,重写相应的魔术方法。

    Person类,属性(name, age, gender)

class Person:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    # 判断两个Person对象的内容是否相等,如果两个Person对象的各个属性值都一样,则返回True,反之返回False。
    def __eq__(self, other):
        # 判断other 是不是 Person
        if isinstance(other, Person):
            return (self.name == other.name and
                    self.age == other.age and
                    self.gender == other.gender)
        return False

    # 重写 __lt__
    def __lt__(self, other):
        if isinstance(other, Person):
            return self.age < other.age
        return "类型不一致,不能比较"

    # 重写 __lt__
    def __le__(self, other):
        if isinstance(other, Person):
            return self.age <= other.age
        return "类型不一致,不能比较"

    # 重写 __ne__
    def __ne__(self, other):
        return not self.__eq__(other)


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


# 没有重写 __eq__ 前, == 比较的是内存地址;重写 __eq__ 后, == 比较的是属性/内容
p1 = Person("smith", 20, "male")
p2 = Person("smith", 20, "male")
dog = Dog("smith", 20, "male")

print(f"p1 == p2: {p1 == p2}")
print(f"p1 == dog: {p1 == dog}")

print(f"p1 < p2: {p1 < p2}")
print(f"p1 <= p2: {p1 <= p2}")
print(f"p1 != p2: {p1 != p2}")

class对象和静态方法

[class 对象](9. 类 — Python 3.13.0 文档)
  1. 基本介绍
    • 类本身也是对象,即:Class对象。
  2. 应用实例
class Monster:
    name = "蝎子精"
    age = 300

    def hi(self):
        print(f"hi() {self.name} {self.age}")


# 下一个断点,可以看到Monster的情况
print(Monster)

# 通过Class对象,可以引用属性(没有创建实例对象也可以引用/访问)
print(f"Monster.name is {Monster.name} Monster.age is {Monster.age}")

# 通过类名如何调用非静态成员方法
Monster.hi(Monster)
[静态方法](描述器指南 — Python 3.13.0 文档)
  1. @staticmethod 将方法转换为静态方法。
  2. 静态方法不会接收隐式的第一个参数,要声明一个静态方法。
class C:
    @staticmethod
    def f(arg1, arg2, argN): ...
  1. 静态方法既可以由类调用(如 C.f() ),也可以由实例中调用(如C().f())。
class Monster:
    name = "蝎子精"
    age = 300

    def hi(self):
        print(f"hi() {self.name} {self.age}")

    @staticmethod
    def ok():
        print("ok()")


# 不需要实例化,通过类即可调用静态方法
Monster.ok()

# 通过实例对象,也可以调用静态方法
monster = Monster()
monster.ok()

[抽象类](abc — 抽象基类 — Python 3.13.0 文档)

抽象类的介绍
  1. 基本介绍
    • 默认情况下,Python不提供抽象类,Python附带一个模块,该模块为定义抽象基类提供了基础,该模块名称为abc
    • 当我们需要抽象基类时,让类继承ABC(abc模块的ABC类),使用@abstractmethod声明抽象方法(@abstractmethod 用于声明抽象方法的装饰器,在abc模块中),那么这个类就是抽象类。
    • 抽象类的价值更多作用是在于设计,是设计者设计好后,让子类基础病实现抽象类的抽象方法。
快速入门
from abc import ABC, abstractmethod


# 将 Animal 做成抽象类,并让子类 Tiger 实现。

# Animal 是抽象类
class Animal(ABC):
    def __init__(self, name, age):
        self.name = name
        self.age = age

    # cry() 是抽象方法
    @abstractmethod
    def cry(self):
        pass


# 注意:抽象类(含有抽象方法),不能实例化。
# TypeError: Can't instantiate abstract class Animal without an implementation for abstract method 'cry'
# animal = Animal("动物", 3)


# 编写子类 Tiger,继承 Animal 并实现抽象方法。
class Tiger(Animal):
    def cry(self):
        print('Tiger is crying')


tiger = Tiger('Tiger', 20)
tiger.cry()
注意事项和细节讨论
  1. 抽象类不能被实例化。
  2. 抽象类需要继承ABC,并且需要至少一个抽象方法。
# 抽象类需要继承`ABC`,并且需要至少一个抽象方法。
from abc import ABC, abstractmethod


class AAA(ABC):
    name = "tim"

    # @abstractmethod
    # def f1(self):
    #     pass


# 如果没有一个抽象方法,能实例化。
obj1 = AAA()
  1. 抽象类中可以有普通方法。
# 抽象类中可以有普通方法。
from abc import ABC, abstractmethod


class AAA(ABC):
    name = "tim"

    @abstractmethod
    def f1(self):
        pass

    def hi(self):
        print("hi()")

    def ok(self):
        pass


class BBB(AAA):
    # 实现父类的f1抽象方法
    def f1(self):
        print("BBB-f1")


obj2 = BBB()
obj2.f1()
obj2.hi()
obj2.ok()
  1. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,否则它仍然是一个抽象类。
# 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,否则它仍然是一个抽象类。
from abc import ABC, abstractmethod


class AAA(ABC):
    name = "tim"

    @abstractmethod
    def f1(self):
        pass

    @abstractmethod
    def f2(self):
        pass

    def hi(self):
        print("hi()")

    def ok(self):
        pass


class BBB(AAA):
    # 实现父类的f1抽象方法
    def f1(self):
        print("BBB-f1")

    # 如果没有完全实现AAA的抽象方法
    # TypeError: Can't instantiate abstract class BBB without an implementation for abstract method 'f2'
    def f2(BBB):
        print("BBB-f2")


obj2 = BBB()
obj2.f1()
obj2.hi()
obj2.ok()
练习题

编写一个Employee类,做成抽象基类,包含如下三个属性:name,id,salary,提供必要的构造器和抽象方法:work()。

对应Manager类来说,他既是员工,还具有奖金(bonus)的属性,请使用继承的思想,设计CommonEmployee类和Manager类,要求实现work(),提示“经理/普通员工 名字 工作中"。

OOP基础 + 抽象类

from abc import abstractmethod, ABC


class Employee(ABC):
    def __init__(self, name, id, salary):
        self.name = name
        self.id = id
        self.salary = salary

    @abstractmethod
    def work(self):
        pass


class CommonEmployee(Employee):
    def work(self):
        print(f"普通员工 {self.name} 工作中")


class Manager(Employee):
    def __init__(self, name, id, salary, bonus):
        super().__init__(name, id, salary)
        self.bonus = bonus

    def work(self):
        print(f"经理 {self.name} 工作中")


manager = Manager("king", "1-111", 10000, 100000)
manager.work()

commonEmployee = CommonEmployee("time", "2-222", 5000)
commonEmployee.work()

最佳实践—模板设计模式

什么是设计模式
  • 设计模式是在大量实践中总结和理论化之后优选的代码结构、编程风格、以及解决思考的思考方式。
  • 设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。
模板设计模式—介绍
  1. 基本介绍
    • 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模版,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
  2. 模板设计模式能解决的问题
    • 当功能内部一部分实现是确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
    • 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
模板设计模式—最佳实践

需求:有多个类,完成不同的任务job,要求统计得到各自完成任务的时间。

  1. 传统方法
import time


class AA:
    def job(self):
        # 得到开始的时间,秒数
        start = time.time()
        num = 0
        for i in range(1, 800001):
            num += 1
        # 得到结束的时间,秒数
        end = time.time()
        print("计算任务执行时间(秒)", (end - start))


class BB:
    def job(self):
        # 得到开始的时间,秒数
        start = time.time()
        num = 1
        for i in range(1, 90001):
            num -= 1
        # 得到结束的时间,秒数
        end = time.time()
        print("计算任务执行时间(秒)", (end - start))


if __name__ == '__main__':
    aa = AA()
    bb = BB()

    aa.job()
    bb.job()
  1. 优化—使用模板设计模式来解决。

编写方法 calc_time(),可以计算某段代码的耗时时间。

编写抽象方法 job()。

编写一个子类,基础抽象类 Template,并实现 job 方法。

image-20241102124958890

import time
from abc import abstractmethod, ABC


class Template(ABC):
    @abstractmethod
    def job(self):
        pass

    # 统计任务执行时间
    def cal_time(self):
        # 得到开始的时间,秒数
        start = time.time()
        self.job()
        # 得到结束的时间,秒数
        end = time.time()
        print("计算任务执行时间(秒)", (end - start))


class AA(Template):
    def job(self):
        num = 0
        for i in range(1, 800001):
            num += 1


class BB(Template):
    def job(self):
        num = 1
        for i in range(1, 90001):
            num -= 1


if __name__ == '__main__':
    aa = AA()
    bb = BB()

    aa.cal_time()
    bb.cal_time()

练习

  1. 定义一个Person类,属性:name,age,job,创建Person,有3个person对象,并按照age从大到小排序。
class Person:
    def __init__(self, name, age, job):
        self.name = name
        self.age = age
        self.job = job

    def __str__(self):
        return f'{self.name}, {self.age}, {self.job}'


p1 = Person('smith', 21, 'python')
p2 = Person('king', 30, 'java')
p3 = Person('tom', 40, '老师')

my_list = [p1, p2, p3]
for p in my_list:
    print(p)

# 实现按照age从大到小排序。

# 解决方案1:冒泡排序
# def bubble_sort(my_list: list[Person]):
#     for i in range(1, len(my_list)):
#         # j变量控制比较的次数,同时可以作为比较元素的索引下标
#         for j in range(len(my_list) - i):
#             # 如果前面的年龄 < 后面的年龄,就交换
#             if my_list[j].age < my_list[j + 1].age:
#                 my_list[j], my_list[j + 1] = my_list[j + 1], my_list[j]


# bubble_sort(my_list)
# print("排序后".center(50, '='))
#
# for p in my_list:
#     print(p)


# 解决方案2:直接使用列表的 .sort
# key=lambda ele: ele.age 表示指定按照列表元素的age属性进行排序。
# reverse=True 表示倒序
print("排序后".center(50, '='))
my_list.sort(key=lambda ele: ele.age, reverse=True)
for p in my_list:
    print(p)
  1. 文件中有 Grand、Father 和 Son,问:父类和子类中通过self和super()都可以调用哪些属性和方法。
class Grand:
    name = 'AA'
    __age = 100

    def g1(self):
        print('Grand-g1()')


class Father(Grand):
    id = '001'
    __score = None

    def f1(self):
        # super() 可以访问哪些成员(属性和方法)
        super().name
        super().g1()

        # self() 可以访问哪些成员
        self.id
        self.__score
        self.name
        self.f1()
        self.g1()

        print('Father-f1()')


class Son(Father):
    name = 'BB'

    def g1(self):
        print('Son-g1()')

    def __show(self):
        # super() 可以访问哪些成员(属性和方法)
        super().id
        super().name
        super().f1()
        super().g1()

        # self() 可以访问哪些成员
        self.name
        self.id

        self.g1()
        self.__show()
        self.f1()

        print('Son-show()')
  1. 编写Doctor类,属性:name, age, job, gender, sal 5个参数的构造器,重写__eq__()方法,并判断测试类中创建的两个对象是否相等(相等就是判断属性是否相同)。
class Doctor:
    def __init__(self, name, age, gender, sal):
        self.name = name
        self.age = age
        self.gender = gender
        self.sal = sal

    def __eq__(self, other):
        if not isinstance(other, Doctor):
            return False
        return self.name == other.name and self.age == other.age and self.sal == other.sal


doctor1 = Doctor('tom', 24, 1, 'male')
doctor2 = Doctor('tom', 24, 1, 'male')
doctor3 = Doctor('tom', 25, 1, 'male')

print(doctor1 == doctor2)
print(doctor1 == doctor3)

欢迎关注我的博客,如有疑问或建议,请随时留言讨论。

标签:__,进阶,Python,self,面向对象编程,print,class,def,name
From: https://blog.csdn.net/qingxuly/article/details/143454260

相关文章

  • Python基础学习(十)面向对象编程(基础)
    代码获取:https://github.com/qingxuly/hsp_python_course完结版:Python基础学习(完结版)面向对象编程(基础)类与对象类与实例的关系类与实例的关系示意图快速入门—面向对象的方式解决养猫问题#定义一个猫类,age,name,color是属性,或者称为成员变量#Cat类就是你......
  • 推荐5个超级实用的 Python 模块,不知道就out啦!
    正文Python标准库有超过200个模块,程序员可以在他们的程序中导入和使用。虽然普通程序员对其中许多模块都有一些经验,但很可能有一些好用的模块他们仍然没有注意到。我发现其中许多模块都包含了在各个领域都非常有用的函数。比较数据集、协作其他函数以及音频处理等都可以仅......
  • [python]Gunicorn加持,轻松提升Flask超7倍性能
    前言之前学习和实际生产环境的flask都是用app.run()的默认方式启动的,因为只是公司内部服务,请求量不高,一直也没出过什么性能问题。最近接管其它小组的服务时,发现他们的服务使用Gunicorn+Flask的方式运行的,本地开发用的gevent的WSGIServer。对于Gunicorn之前只是耳闻,没实际用过,正......
  • PyCharm 2024.1 解锁版 (Python集成开发IDE)详细安装步骤
    分享文件:PyCharm2024.1解锁版(Python集成开发IDE)链接:https://pan.xunlei.com/s/VOAa_CiVVvZnyQgLfpmCIOABA1提取码:cx4h安装步骤1、下载解压后点击如下进行安装2、选择安装路径3、默认勾选将PyCharm创建桌面快捷方式4、默认,点击【安装】5、安装中,耐心等待2-5分......
  • Python自动化运维:技能掌握与快速入门指南
    #编程小白如何成为大神?大学生的最佳入门攻略#在当今快速发展的IT行业中,Python自动化运维已经成为了一个不可或缺的技能。本文将为您详细介绍Python自动化运维所需的技能,并提供快速入门的资源,帮助您迅速掌握这一领域。必备软件工具1.Python与PyCharmPython和PyCharm是自动......
  • Python 函数综合案例
    设计格黑娜学院银行ATM定义一个全局变量:money,用来记录银行卡余额(默认5000000)定义一个全局变量:name,用来记录客户姓名(启动程序时输入)定义如下的函数:查询余额函数存款函数取款函数主菜单函数要求.程序启动后要求输入客户姓名.查询余额,存款,取款后都会返回主菜单.......
  • 【docker】—— 部署python项目
    使用docker部署python项目这里默认已经安装好docker并进行了相关配置,只演示使用docker打包并部署一个简单的python项目。更多docker基本操作参考:【docker】——docker常用操作下面是准备好的项目和程序:项目结构:项目:DOCKER_PYTHON_TESE文件:main.ipynb代码如下:#......
  • python - 小袁口算ocr
    按照模拟器位置捕捉截图,使用paddleocr识别数字后比较大小importpyautogui,timefrompaddleocrimportPaddleOCRpaddleocr=PaddleOCR(lang='en',show_log=False,use_angle_cls=True)defcapture_screenshot(region=None):img=pyautogui.screenshot(region=regio......
  • 学习python的第十一天
    今天学习了正则有以下思维导图 对于以上内容,有以下笔记,以及关于元字符的图importfunctools#re.findallimportre#a="python12314534564java"#anqi=re.findall("123",a)#(匹配规则,数据)#print(anqi)#re.match是从一开始就开始匹配#print(re.match("python"......
  • python脚本结合计算引擎快速获取亚马逊的父ASIN并保存到数据库
    按照亚马逊的规则,一个ASIN只能有一个父ASIN,在某些情况下不得分析asin的所属变体父asin是否同一个。Python代码。  importop_data_web_hookaswebhooktable_id_dp="xxxx"defget_parent_asin_by_page_source(res):ifresisNone:return""try:......