【python】Class(11)

本文介绍了 Python 面向对象编程的基础概念,包括类、对象、方法等基本元素,并详细解析了封装、继承和多态等核心特性。通过丰富的示例代码帮助读者理解和掌握面向对象编程的基本思想。

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

在这里插入图片描述

参考+借鉴+模仿+学习



object oriented programming (OOP)面向对象编程

  • 类:对具有相同数据和方法的一组对象的描述或定义。
  • 对象:对象是一个类的实例。
  • 实例(instance):一个对象的实例化实现。
  • 标识(identity):每个对象的实例都需要一个可以唯一标识这个实例的标记。
  • 实例属性(instance attribute):一个对象就是一组属性的集合。
  • 实例方法(instance method):所有存取或者更新对象某个实例一条或者多条属性的函数的集合。
  • 类属性(classattribute):属于一个类中所有对象的属性,不会只在某个实例上发生变化
  • 类方法(classmethod):那些无须特定的对性实例就能够工作的从属于类的函数。

1、类、对象、方法

# 定义一个类
class Car:
    def drive(self):
        print('我正在开车') 
    def turnover(self):
        print('翻车了')
# 创建一个对象
xx = Car()
# 调用类方法
xx.drive()
xx.turnover()

结果为

我正在开车
翻车了

2、类的属性

可以在类中定义属性,也可以在对象中添加属性

class Car:
    def drive(selt):
        print('我正在开车')
    
    def turnover(self):
        print('翻车了')
        
#创建一个对象
xiao_jie_jie = Car()
xiao_jie_jie.drive()#调用xiao_jie_jie指向的对象的方法
xiao_jie_jie.turnover()

#添加属性,属性就是变量
xiao_jie_jie.name = '小红'
xiao_jie_jie.age = 18
print ("%s的年龄是%d"%(xiao_jie_jie.name,xiao_jie_jie.age))

结果为

我正在开车
翻车了
小红的年龄是18

3、封装、继承、多态

3.1 封装(Encapsulation)

封装:就是隐藏对象的属性和实现细节,仅对外提供公共访问方式(对外部隐藏对象的工作细节)

list1 = [1,3,2,7,5]
list1.sort()
list1

output

[1, 2, 3, 5, 7]

list1是列表list的实例对象,我们调用了sort方法()

3.2、继承(Inheritance)

子类自动共享父类之间数据和方法的机制

如果一个类 A 继承自另一个类 B,就把这个 A 称为 B 的子类,把 B 称为 A 的父类、基类或超类。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码(偷懒)。

在子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。另外,为子类追加新的属性和方法也是常见的做法。

class list2222 (list):
    pass
list2 = list2222()
list2.append(1)
list2.append(3)
list2.append(2)
list2.append(4)
print(list2)
list2.sort()
print(list2)

output

[1, 3, 2, 4]
[1, 2, 3, 4]

list2222继承list类,实例对象list2,也可以调用list的append()、sort()方法


1)如果子类和父类的方法同名,子类会覆盖父类

# 如果子类和父类的方法同名,子类会覆盖父类
class Parent:
    def hello(self):
        print("****")
class Child(Parent):
    def hello(self):
        print("####")

p = Parent()
p.hello()
c = Child()
c.hello()

Output

****
####

2)多层继承

# 多层继承 class A(B,C,D……)
class Base1:
    def fool1(self):
        print("i'm a fool")

        
class Base2:
    def fool2(self):
        print("i'm a fool,too")

class A(Base1,Base2):
    pass
   
a = A()
a.fool1()
a.fool2()

Output

i'm a fool
i'm a fool,too

多重继承使用不当会导致重复调用(也叫 钻石继承、菱形继承)的问题,关于__init__的说明请参考本博客的附录

class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")
        
class B(A):
    def __init__(self):
        print("进入B…")
        A.__init__(self)
        print("离开B…")

class C(A):
    def __init__(self):
        print("进入C…")
        A.__init__(self)
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        B.__init__(self)
        C.__init__(self)
        print("离开D…")

实例化D的时候

d = D()

output

进入D…
进入B…
进入A…
离开A…
离开B…
进入C…
进入A…
离开A…
离开C…
离开D…

进入了两次A。
这有什么危害?我举个例子,假设 A 的初始化方法里有一个计数器,那这样 D 一实例化,A 的计数器就跑两次(如果遭遇多个钻石结构重叠还要更多),很明显是不符合程序设计的初衷的(程序应该可控,而不能受到继承关系影响)

为了让大家都明白,这里只是举例最简单的钻石继承问题,在实际编程中,如果不注意多重继承的使用,会导致比这个复杂N倍的现象,调试起来不是一般的痛苦……所以一定要尽量避免使用多重继承。

如何解决这种问题呢,super()大发神威

class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")

class B(A):
    def __init__(self):
        print("进入B…")
        super().__init__()
        print("离开B…")

class C(A):
    def __init__(self):
        print("进入C…")
        super().__init__()
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        super().__init__()
        print("离开D…")

实例化

d = D()

output

进入D…
进入B…
进入C…
进入A…
离开A…
离开C…
离开B…
离开D…

3)继承__init__

class Fish1:
    def __init__(self):
        self.length = 10
        self.weight = 5
    def eat(self):
        self.weight+=1
        print("当前的weight:",self.weight)
    def growth(self):
        self.length+=1
        print("当前的length:",self.length)

class Fish2(Fish1):
    pass

class Fish3(Fish1):
    def __init__(self):
        self.hungry = True
        #Fish1.__init__(self)
        #super().__init__()
    def eat(self):
        if self.hungry:
            print("开吃")
        else:
            print("吃饱了")

测试

f2 = Fish2()
f2.eat()
f2.growth()

f3 = Fish3()
f3.eat()
f3.growth()

output

当前的weight: 6
当前的length: 11
开吃
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-8ce1c7ea1927> in <module>()
      5 f3 = Fish3()
      6 f3.eat()
----> 7 f3.growth()

<ipython-input-5-2ff9b0c109ac> in growth(self)
      7         print("当前的weight:",self.weight)
      8     def growth(self):
----> 9         self.length+=1
     10         print("当前的length:",self.length)
     11 

AttributeError: 'Fish3' object has no attribute 'length'

报错了,没有 length 的属性,没有继承父类的__init__,有如下两种解决方法

  • 调用未绑定的父类方法
    在子类的__init__函数下添加 Fish1.__init__(self),不推荐这种方法,因为如果改了继承类还需要修改父类Fish1的名字

  • 使用super函数
    在子类的 __init__ 函数下添加 super().__init__(),推荐这种方法,会自己找继承的父类

修改后的output

当前的weight: 6
当前的length: 11
开吃
当前的length: 11

parent

使用super()或父类的名称调用父类的初始化

super

class Parent:
    def __init__(self, city, address):
        self.city = city
        self.address = address

class Child(Parent):
    def __init__(self, city, address, university):
        super().__init__(city, address) # 调用父类的初始化方法
        self.university = university

child = Child('A', 'B', 'C')
print(child.city, child.address, child.university)  # A B C

父类的名字

class Parent:
    def __init__(self, city, address):
        self.city = city
        self.address = address

class Child(Parent):
    def __init__(self, city, address, university):
        Parent.__init__(self, city, address) # 调用父类的初始化方法,区别于 super,这里需要有 self
        self.university = university

child = Child('A', 'B', 'C')
print(child.city, child.address, child.university)  # A B C

3.3、多态(Polymorphism)

可以对不同类的对象调用相同的方法,产生不同的结果

class A:
    def fun(self):# 记得要加self
        print('i love you')
class B:
    def fun(self):# 记得要加self
        print('i love x')
a = A()
b = B()
a.fun()
b.fun()

output

i love you
i love x

3.4、重载(Overloading)

重载是指在同一个类中允许有多个方法具有相同的名字,但参数列表不同(参数的数量或类型不同)。重载用于提供不同的方法来处理不同的输入参数。

特点:

  • 编译时多态:方法的选择在编译时决定。
  • 方法签名:重载的方法必须具有不同的参数列表。
  • 灵活性:允许使用相同的名字来实现不同的功能。
__eq__() for ==
__sub__() for -
__mul__() for *
__truediv__() for /
__ne__() for !=
__ge__() for >=
__gt__() for >

eg

class Journey:
    def __init__(self, location, destination, duration):
        self.location = location
        self.destination = destination
        self.duration = duration

    def __eq__(self, other):
        return ((self.location == other.location) and
                (self.destination == other.destination) and
                (self.duration == other.duration))


first = Journey('Location A', 'Destination A', '30min')
second = Journey('Location B', 'Destination B', '30min')

print(first == second)

总结

  • 封装:隐藏内部实现细节,通过公共接口访问数据。
  • 继承:允许子类继承父类的属性和方法,实现代码重用。
  • 多态:允许将子类对象视为父类对象来使用,实现动态方法调用。
  • 重载:在同一个类中允许方法名相同但参数列表不同的方法。

4、公有和私有

class Person:
    name = '小明'
person = Person()
person.name

output

'小明'

python属性和方法都是公有的,有 name mangling 机制(在变量名或者函数名之前加上“__”两个下划线,那么这个变量或者函数就会变为私有的了),如下

如果想要在外部访问,那么只需要在名称前面加上 ‘_类名’ 变成 ‘_类名__名称’。

class Engineer:
    def __init__(self, name):
        self.name = name
        self.__starting_salary = 10

dain = Engineer('Dain')
print(dain._Engineer__starting_salary)  # 10
print(dain.Engineer__starting_salary)  # 报错

output

Traceback (most recent call last):
  File "/usercode/file.py", line 9, in <module>
    print(dain.Engineer__starting_salary)  # 报错
AttributeError: 'Engineer' object has no attribute 'Engineer__starting_salary'

再看看下面的例子

class Person:
    __name = '小明'

试试

person = Person()
person.name

output

AttributeError: 'Person' object has no attribute 'name'

再试试

person = Person()
person.__name

output

AttributeError: 'Person' object has no attribute '__name'

都报错了,只能内部访问,外部不行,可以通过定义函数的方式访问

class Person:
    __name = '小明'
    def get_name(self):
        return self.__name
    
person = Person()
person.get_name()

output

'小明'

但是,name mangling 其实是伪私有,我们可以通过_类名__变量名访问,当然我们并不提倡这种抬杠较真粗暴不文明的访问形式……

class Person:
    __name = '小明'
person = Person()
person._Person__name

output

'小明'

Note:python的私有机制是伪私有

5、组合

把类的实例化,放在一个新的类里面,横向之间关系的类用 组合,纵向的用继承

Python 继承机制很有用,但容易把代码复杂化以及依赖隐含继承。因此,经常的时候,我们可以使用组合来代替。在Python里组合其实很简单,直接在类定义中把需要的类放进去实例化就可以了。

class Turtle:
    def __init__(self,x):
        self.num = x 

class Fish:
    def __init__(self,x):
        self.num = x   
        
# 组合,把类的实例化,放在一个新的类里面
class Pool:
    def __init__(self,x,y):
        self.turtle = Turtle(x)
        self.fish = Fish(y)
        
    def print_num(self):
        print("水池里面有乌龟%d 个,有鱼%d个"%(self.turtle.num,self.fish.num))
pool = Pool(1,2)
pool.print_num()

output

水池里面有乌龟1 个,有鱼2

下面看一个稍微复杂的例子,在附录魔法方法 __str__小姐姐煮面 例子的基础上改进,这里是买房,构建两个类,一个是房子,一个是家具,都有面积属性,随着家具的增加,房子空余面积变小

class Home:
    def __init__(self, area, info, address):
        self.area = area # 房子的面积
        self.info = info # 房子的户型
        self.address = address # 房子的地址
        self.left_area = area # 房子空余面积
        self.furnitures = [] # 房子中的家具

    def __str__(self):
        return "房子的面积:{},户型:{},地址:{},现有家具:{},剩余面积:{}".format(self.area, self.info, self.address, self.furnitures,
                                                             self.left_area)

    def add_furnitures(self, item): # 添加家具
        self.left_area -= item.area # 房屋空余面积相应减少
        self.furnitures.append(item.furniture) # 新增房子中的家具

class Furniture:
    def __init__(self, furniture, area):
        self.furniture = furniture # 家具的名字
        self.area = area # 家具的面积

    def __str__(self):
        return "家具{}的面积为{}m2".format(self.furniture, self.area)


home = Home(120, '三室一厅', '上海市 黄浦区 人民大道 666号')
print(home)

bed = Furniture('双人豪华大床', 6)
print(bed)

home.add_furnitures(bed)
print(home)

aircondition = Furniture('立式空调', 1)
home.add_furnitures(aircondition)
print(home)

output

房子的面积:120,户型:三室一厅,地址:上海市 黄浦区 人民大道 666,现有家具:[],剩余面积:120
家具双人豪华大床的面积为6m2
房子的面积:120,户型:三室一厅,地址:上海市 黄浦区 人民大道 666,现有家具:['双人豪华大床'],剩余面积:114
房子的面积:120,户型:三室一厅,地址:上海市 黄浦区 人民大道 666,现有家具:['双人豪华大床', '立式空调'],剩余面积:113

6、属性和方法名相同,方法会被覆盖

class ABC:
    def x (self):
        print("hello")
abc = ABC()
abc.x()
abc.x = 1
print(abc.x)
abc.x()

output

hello
1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-21-3e5e59179b1f> in <module>()
      6 abc.x = 1
      7 print(abc.x)
----> 8 abc.x()

TypeError: 'int' object is not callable

7、绑定

Python 严格要求方法需要有实例才能被调用,这种限制其实就是Python所谓的绑定概念

class BB:
    def printBB():
        print("hello")
BB.printBB()
bb=BB()
bb.printBB()

output

hello
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-22-dbb760d5e8f1> in <module>()
      4 BB.printBB()
      5 bb=BB()
----> 6 bb.printBB()

TypeError: printBB() takes 0 positional arguments but 1 was given
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值