Python面向对象的特性

本文详细介绍了Python面向对象编程的特性,包括私有化、封装、继承、多态和单例模式。私有化通过定义私有属性和方法来保护数据的安全;封装是将属性和方法封装到类中,隐藏实现细节;继承允许创建子类,扩展基类的功能;多态允许使用统一的接口调用不同对象的方法;单例模式确保一个类只有一个实例。同时,文章还讨论了深度优先和广度优先在类方法查找中的应用。

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

私有化

如果有一个对象,当需要对其进行修改属性时,有二种方法:

  • 对象名.属性名 = 数据 ------>直接修改;
  • 对象名.方法名()------->间接修改;

为了更好的保护属性安全,既不能随意修改,一般的处理方式为:

  • 将属性定义为私有属性;
  • 添加一个可以调用的方法,供调用;
  • 私有变量在外界是不会被修改的
  • 如果想修改,就需要定义set函数
  • get函数是用来获取私有变量的
class Person:
    def __init__(self,name,age):
        self.__name = name
        self.__age = age
        self.__score = 59

    def setAge(self,age):
        if age > 0 and age  <=120:
            self.__age=age
        else:
            print('年龄不在规定范围内')
  
    def getAge(self):
        return self.__age

    def __str__(self):
        return '姓名是:{},年龄是:{}'.format(self.__name,self.__age)
p = Person('小明',18)
print(p)
p.name='小强'    #外界改变不生效
print(p)

在这里插入图片描述

  • 定义一个私有变量,
p = Person('xiaoming',18)
print(p)
p.setAge(20)     #通过set函数改变
print(p)
print('p的你年龄是:',p.getAge())  #获取私有变量

在这里插入图片描述

print(dir(Person)) #可以看到类中自带的魔术方法
#print(p.__dir__())   
print(dir(p))    

在这里插入图片描述

  • 在类中定义的方法,变量统称为attribute,可以用dir方法进行查看
  • python解释器在底层对私有变量进行了重命名,将__age重命名为_person__age

使用装饰器

  • 需要添加装饰器
  • 可以使私有变量被调用的形式和和普通变量一样
class Person:
    def __init__(self,name,age):
        self.name = name
        self.__age = age

    @property      #相当于get函数,要先定义
    def age(self):
        return self.__age

    @age.setter    #相当与set函数,要和上面的定义同步
    def age(self,age):
        if age > 0 and age  <=120:
            self.__age=age
        else:
            print('年龄不在规定范围内')

    def __str__(self):
        return '姓名是:{},年龄是:{}'.format(self.name,self.__age)

p = Person('小明',18)
print(p.age)
p.age=120
print(p.age)

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

关联关系

import random
class Road:
    def __init__(self,name,len):
        self.name = name
        self.len = len
        
class Car:
    def __init__(self,brand,speed):
        self.brand = brand
        self.speed = speed

    def get_time(self,road):
        ran_time = random.randint(1,10)
        msg = '{}品牌的车在以{}速度行驶{}小时'.format(self.brand,road.name,self.speed,ran_time)
        print(msg)

    def __str__(self):
        return  '{}品牌的,速度:{}'.format(self.brand,self.speed)

r = Road('京藏高速',12000)
r.name = '京哈高速'
print(r.name)

audi = Car('奥迪',120)
print(audi)
audi.get_time(r)

在这里插入图片描述

class Computer:
    def __init__(self,brand,type,color):
        self.brand = brand
        self.type = type
        self.color = color

    def online(self):
        print('正在用电脑上网')

    def __str__(self):
        return self.brand + '---' + self.type + '---' + self.color

class Book:
    def __init__(self,bname,author,number):
        self.bname = bname
        self.author = author
        self.number = number

    def __str__(self):
        return self.bname +'---'+ self.author+'---'+str(self.number)

class Student:
    def __init__(self,name,computer,book):
        self.name = name
        self.computer = computer
        self.books = []
        self.books.append(book)

    def borrow_book(self,book):
        for book1 in self.books:
            if book1.bname == book.bname:
                print('已借过此书')
                break
        else:
            self.books.append(book)
            print('添加成功')

    def show_book(self):
        for book in self.books:
            print(book.bname)

    def __str__(self):
        return self.name + '---' + str(self.computer)+'---'+str(self.books)
computer = Computer('mac','mac pro 2020','灰色')
book = Book('盗墓笔记','南派三叔',10)
stu = Student('xiaoming',computer,book)
print(stu)
book1 =Book('斗破苍穹','天蚕土豆',8)
stu.borrow_book(book1)
stu.show_book()

在这里插入图片描述

封装

  1. 封装是面向对象编程的一大特点
  2. 面向对象编程的第一步 将属性和方法封装到一个抽象的类中
  3. 外界使用类创建对象,然后让对象调用方法
  4. 对象方法的细节都被封装在类的内部
  • 将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。

  • “封装”就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体(即类);封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,一特定的访问权限来使用类的成员。

  • 而这些封装数据的函数是和Student类本身是关联起来的,我们称之为类的方法。那如何定义类的方法呢?

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

    def __str__(self):
        string = '我的名字是{},体重为{}'.format(self.name, self.weight)
        return string

    def run(self):
        print('%s去跑步。。。' % self.name)
        self.weight -= 0.5

    def eat(self):
        print('%s  去吃东西。。。。。' % self.name)
        self.weight += 1

p = Person('小明', 75)
p.run()
p.eat()
print(p)

在这里插入图片描述
案例解析

  1. 房子有户型,总面积和家具名称列表
    新房子是没有家具的
  2. 家具有名字和占地面积,其中
    eg:占地 6平方米
  3. 将以上三件家具添加到房子中
  4. 打印房子的时候,要求输出:户型 总面积 剩余面积 家具名称列表
class HouseItem:
    def __init__(self, name, area):
        self.name = name
        self.area = area

    def __str__(self):
        return '[%s] 占地 %.2f' % (self.name, self.area)
class House:
    def __init__(self, house_type, area):
        self.house_type = house_type
        self.area = area
        self.free_area = area
        self.item_list = []

    def __str__(self):
        return '户型:%s \n总面积:%.2f [剩余:%.2f] \n家具:%s' % (self.house_type, self.area, self.free_area, self.item_list)

    def add_item(self, item):
        if item.area > self.free_area:
            print('%s 的面积太大,无法添加' % item.name)
            return
        self.item_list.append(item.name)
        self.free_area -= item.area
bed = HouseItem('床', 400)
chest = HouseItem('柜子', 2)
table = HouseItem('桌子', 4.5)
my_home = House('两室一厅', 200)
print(bed, chest, table, my_home)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)

在这里插入图片描述

  1. 士兵瑞恩有一把AK47
  2. 士兵可以开火(士兵开火扣动的是扳机)
  3. 枪 能够 发射子弹(把子弹发射出去)
  4. 枪 能够 装填子弹 --增加子弹的数量
class Gun:
    def __init__(self, model):
        self.model = model
        self.bullet_count = 0

    def add_bllet(self, count):
        self.bullet_count += count

    def shoot(self):
        if self.bullet_count <= 0:
            print('%s 没有子弹了。。。。' % self.model)
            return
        self.bullet_count -= 1
        print('%s--------%s' % (self.model, self.bullet_count))

class Soldier:
    def __init__(self, name):
        self.name = name
        self.gun = None

    def fire(self):
        if self.gun == None:
            print('%s 没有枪。。。' % self.name)
            return
        self.gun.add_bllet(50)
        self.gun.shoot()

ak = Gun('ak47')
ryan = Soldier('Ryan')
ryan.gun = ak
ryan.fire()
ryan.fire()

在这里插入图片描述

在这里插入图片描述

继承

  • 面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

  • 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”,继承的过程,就是从一般到特殊的过程。在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

  • 继承概念的实现方式主要有2类:实现继承、接口继承。

  • 实现继承是指使用基类的属性和方法而无需额外编码的能力。

  • 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法)。

继承类的构造方法:

  1. 经典类的写法: 父类名称.init(self,参数1,参数2,…)

  2. 新式类的写法:super(子类,self).init(参数1,参数2,…)

在这里插入图片描述

深度优先和广度优先

python3一般是广度优先

经典类:


class P1: 
     def foo(self):           
         print 'p1-foo' 
 
class P2 : 
     def foo(self): 
         print 'p2-foo' 
 
     def bar(self): 
         print 'p2-bar' 
 
class C1 (P1,P2): 
     pass  
 
class C2 (P1,P2): 
     def bar(self): 
         print 'C2-bar'   
 
class D(C1,C2): 
     pass
d = D()
d.foo() # 输出 p1-foo 
d.bar() # 输出 p2-bar
  • 实例d调用foo()时,搜索顺序是 D => C1 => P1
  • 实例d调用bar()时,搜索顺序是 D => C1 => P1 => P2
  • 换句话说,经典类的搜索方式是按照“从左至右,深度优先”的方式去查找属性。d先查找自身是否有foo方法,没有则查找最近的父类C1里是否有该方法,如果没有则继续向上查找,直到在P1中找到该方法,查找结束。
  • 深度优先算法:深度优先搜索算法(Depth-First-Search),是搜索算法的一种。
  • 是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。
  • 这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。
  • 属于盲目搜索
    在这里插入图片描述
    新式类:
class P1(object): 
     def foo(self):           
         print 'p1-foo' 
 
class P2(object):
     def foo(self): 
         print 'p2-foo' 
 
     def bar(self): 
         print 'p2-bar' 
 
class C1 (P1,P2): 
     pass  
 
class C2 (P1,P2): 
     def bar(self): 
         print 'C2-bar'   
 
class D(C1,C2): 
     pass
d=D() 
d.foo() # 输出 p1-foo 
d.bar() # 输出 c2-bar
  • 实例d调用foo()时,搜索顺序是 D => C1 => C2 => P1
  • 实例d调用bar()时,搜索顺序是 D => C1 => C2
  • 可以看出,新式类的搜索方式是采用“ 广度优先”的方式去查找属性。
  • 广度优先算法:广度优先算法(Breadth-First Search),同广度优先搜索,又称作宽度优先搜索,或横向优先搜索,简称BFS,是一种图形搜索演算法。
  • 简单的说,BFS(也是一种盲目搜寻法)是从根节点开始,沿着树的宽度遍历树的节点,如果发现目标,则演算终止。
  • 广度优先搜索的实现一般采用open-closed表。
  • 目的是系统地展开并检查图中的所有节点,以找寻结果。
  • 换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。
    在这里插入图片描述

无参数继承

class Person:
    def __init__(self):
        self.name='小明'
        self.age=18

    def eat(self):
        print(self.name+'正在吃饭')

    def run(self):
        print(self.name+'正在跑步')
class Student(Person):
    def __init__(self):
        print('----->student的init')
        super().__init__()
s=Student()
s.run()

在这里插入图片描述

有参数继承

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

    def eat(self):
        print(self.name+'正在吃饭')

    def run(self):
        print(self.name+'正在跑步')

class Student(Person):
    def __init__(self,name):
        print('----->student的init')
        super().__init__(name)


s=Student('jack')
s.run()

每个子类有独特的属性

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

    def eat(self):
        print(self.name + '正在吃饭')

    def run(self):
        print(self.name + '正在跑步')
class Student(Person):
    def __init__(self, name, age, class1):
        super().__init__(name, age)           #1
        self.class1 = class1

    def study(self, course):                  #2
        print('{}正在学习{}课程'.format(self.name, course))

    def eat(self, food):
    	super().eat()                         #3
        print('{}喜欢吃{}'.format(self.name, food))
   
    def __str__(self):
        return '学生的姓名:{} 年龄:{} 班级:{}'.format(self.name, self.age, self.class1)
class Employee(Person):                      
    def __init__(self, name, age, salary, manager):
        super().__init__(name, age)
        self.salary = salary
        self.manager = manager

    def __str__(self):
        return '雇员的姓名:{} 年龄:{} 薪资:{} 老板:{}'.format(self.name, self.age, self.salary, self.manager)
s = Student('jack', 12, 'python1')
e = Employee('tom', 22, 10000, 'king')
print(s)
print(e)
s.run()
s.study('python基础')
s.eat('满汉全席')
  1. 子类可以通过super函数继承父类的属性,并且可以定义自己的属性
  2. 子类可以定义自己的方法
  3. 子类可以使用父类的方法,也可以重写父类的方法,注意:改重写的方法名必须和父类方法同名

在这里插入图片描述
案例解析

编写一个简单的工资管理程序,系统可以管理以下四类人:工人(worker)、销售员(salesman)、经理(manager)、
销售经理(salemange)。所有的员工都具有员工号,姓名,工资等属性,有设置姓名,获取姓名,获取员工号,计算工资等方法。

  1. 工人:工人具有工作小时数和时薪的属性,工资计算法方法为工作小时数*时薪。
  2. 销售员;具有销售额和提成比例的属性,工资计算方法为销售额+提成比例。
  3. 经理:具有固定月薪的属性,工资计算方法为固定月薪。
  4. 销售经理:工资计算方法为销售额*提成比例+固定月薪。

请根据以上要求设计合理的类,完成以下功能:

  1. 添加所有类型的人员
  2. 计算月薪
  3. 显示所有人工资情况
class Person:
    def __init__(self, no, name, salary):
        self.no = no
        self.name = name
        self.salary = salary

    def __str__(self):
        msg = '工号:{} 姓名:{} 本月工资:{}'.format(self.no, self.name, self.salary)
        return msg

    def getSalary(self):
        return self.salary

class Worker(Person):
    def __init__(self, no, name, salary, hours, per_hour):
        super().__init__(no, name, salary)
        self.hours = hours
        self.per_hours = per_hour

    def getSalary(self):
        money = self.hours * self.per_hours
        self.salary += money
        return self.salary

class Salesman(Person):
    def __init__(self, no, name, salary, salemoney, percent):
        super().__init__(no, name, salary)
        self.salemoney = salemoney
        self.percent = percent

    def getSalary(self):
        money = self.salemoney * self.percent
        self.salary += money
        return self.salary
w=Worker('001','king',2000,160,50)
s=w.getSalary()
print('月薪:',s)
print(w)
print('-------------------------------')
saler = Salesman('002','lucy',5000,5000000,0.003)
s1=saler.getSalary()
print('月薪:',s1)
print(saler)

在这里插入图片描述

多重继承

class A:
    def test(self):
        print('------>AAAAA')

class B:
    def test1(self):
        print('------>BBBBB')

class C(A,B):
    def test2(self):
        print('------>CCCCC')
c = C()
c.test()
c.test1()
c.test2()

在这里插入图片描述
查看继承顺序

print(inspect.getmro(C))
print(C.__mro__)

在这里插入图片描述

在这里插入图片描述

多态

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

  • 多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数。
  • 在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。
  • 也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

多态性的好处

  • 增加了程序的灵活性,以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal)
  • 增加了程序额可扩展性,通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用
class Person:
    def __init__(self, name):
        self.name = name

    def feed_pet(self, pet):
        if isinstance(pet, Pet):   #判断这个object是不是该类子类的对象
            print('{}喜欢养宠物{},昵称是{}'.format(self.name, pet.role, pet.nickname))
        else:
            print('不是宠物')

class Pet:
    role = 'Pet'

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

    def show(self):
        print('昵称:{},年龄:{}'.format(self.nickname, self.age))

class Cat(Pet):
    role = '猫'

    def catch_mouse(self):
        print('抓老鼠')


class Dog(Pet):
    role = '狗'

    def watch_house(self):
        print('看房子')


class Tiger:
    def eat(self):
        print('危险')

cat = Cat('花花', 2)
dog = Dog('旺旺', 4)
tiger = Tiger()
p = Person('小明')
p.feed_pet(cat)
p1 = Person('小强')
p1.feed_pet(tiger)

在这里插入图片描述

在这里插入图片描述

单例模式

设计模式

  • 设计模式是前人工作的总结和提炼,通常,被人们广泛流传的设计模式都
  • 是针对某一特定问题的成熟解决方案
    • 使用设计模式是为了可重用代码,让代码更容易被他人理解,
  • 保证代码可靠性

单例设计模式

  • 目的:让类创建对象,在系统中只有唯一的一个实例(对象)
  • 每一次执行类名()返回的对象 内存地址是相同的

我们用 类名. 的方式创建对象的时候,python解释器会帮我们做两件事情

  1. 为对象分配空间
  2. 对象初始化

使用类名()创建对象时,python的解释器首先会调用__new__方法为对象分配空间__new__是一个由object基类提供的内置的静态方法,主要有两个作用:

  • 在内存中为对象分配空间
  • 返回对象的引用
  • python的解释器获得对象的引用后,将引用作为第一个参数,传递给__init__方法
  • new:负责给对象分配空间 init(初始化方法)负责给对象初始化
  • 我们要重写new方法,使用类名()创建对象的时候,返回的都是同一个地址

重写__new__方法的代码非常固定:

  • 继承自父类方法
  • 返回父类方法调用_new__方法的结果
  • 重写__new__方法一定要return object.new(cls),否则python的解释器得不到分配了空间的对象引用,就不会调用对象的初始化方法
class Singleton:
    __instance = None

    def __new__(cls, *args, **kwargs):
        print('------>new')
        if cls.__instance is None:
            print('---->1')
            cls.__instance = object.__new__(cls)
            print(cls.__instance)
            return cls.__instance
        else:
            print('------>2')
            return cls.__instance

s = Singleton()
s1 = Singleton()
print(s)
print(s1)

在这里插入图片描述

可见,两个实例申请的内存地址是一样的,即new方法返回的地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值