欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

零基础小白的Python学习之路(六)面向对象编程

程序员文章站 2022-03-23 09:12:35
面向对象...


参考资料:《Python编程 从入门到实战》+ 尚学堂Python400集

面向对象编程

面向对象(Object oriented Programming,OOP)编程的思想主要是针对大型软件设计而来,增强程序的扩展性、可读性。
面向对象编程将数据和操作数据相关的方法封装到对象中,组织代码和数据的方式更加接近人的思维,从而大大提高编程效率
Python完全采用了面向对象的思想,是真正面向对象的编程语言,完全支持面向对象的基本功能(继承、多态、封装等)
Python中,一切皆对象。

Python支持:

  • 面向过程
  • 面向对象
  • 函数式编程

面向对象 VS 面向过程

面向过程(Procedure Oriented)

程序的逻辑流程,执行者思维,适合编写小规模的程序
思考怎么按步骤实现,第一步该干什么,第二步该干什么,等等。但是面对大型复杂的程序时,需要多方协作,这时面向过程就不再适用了

面向对象(Object oriented)

面向对象更关注“软件中的对象之间的关系”,是一种“设计者”思维,适合编写大规模程序,这更契合人的思维模式,首先思考怎么设计这个东西,这是一种不同于面向过程的思维上的转变。

遇到复杂问题,先从问题中找名词(面向过程更多的是找动词),然后确立这些名词哪些可以作为类,再根据问题需求确定类的属性和方法,确定类之间的关系。

比如举个例子,造车。汽车内部具有非常复杂的结构,我们想简单通过面向过程去实现是很困难的,采用面向对象就好很多,首先我们会思考一部跑车由哪些元素 / 部件构成,比如发动机、轮胎、车壳、座椅、挡风玻璃,一些高性能配置还需额外的部件。
为了充分体现协作的好处,我们需要找各自负责的厂家进行各自零部件(对象)的设计与制造,同一段时间,各大厂商同时进行各自部件的制造(方法),大大提高了生产效率,但要注意的是,细化到每个零部件的设计制造,同样是一种流水线作业,依旧是面向过程的思维,但是宏观上由面向对象去把握,细化到每一个部件有具体的方法去实现,这就是面向对象的过程以及好处,同时面向对象与面向过程相辅相成,并不是独立的。

【总结】

  • 两者都是解决问题的思维方式,都是代码组织的方式
  • 解决简单问题可以使用面向过程
  • 解决复杂问题:宏观上使用面向对象把握,微观处理上仍然是面向过程

面向对象三大特征

  • 封装(隐藏)
  • 继承
  • 多态

封装(隐藏)

隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。
Python在这一部分的实现更多的是由程序员在编程的时候去自觉实现,没有严格的语法级别的“访问控制符”

继承

继承可以让子类具有父类的特性,提高了代码的重用性
从设计上来看,这是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法

多态

指同一个方法调用由于对象不同会产生不同的行为。

【继承】

语法格式:
Python支持多重继承,一个子类可以继承多个父类

class 子类类名(父类1 [, 父类2, …]):
 类体

如果在类定义中没有指定父类,则默认父类是object类。也就是说,object是所有类的父类,里面定义了一些所有类共有的默认实现,比如:__new __()等

定义子类时,必须在其构造函数中调用父类的构造函数
基本格式:

父类名.__init __(self, 参数列表)

【测试类之间关系】

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

    def say_age(self):
        print(self.name,"的年龄是:",self.__age)

class Student(Person): # Student继承了Person,Person继承了object
    def __init__(self,name,age,score):
        self.score = score
        Person.__init__(self,name,age)
        
print(Student.mro())

零基础小白的Python学习之路(六)面向对象编程
Student继承了Person,Person继承了object

【测试2】

class Person:
    def __init__(self,name,age):
        self.name = name
        self.__age = age # 注意是私有的 虽然被子类继承了,但不能直接用

    def say_age(self):
        print(self.name,"的年龄是:",self.__age)

class Student(Person): # Student继承了Person,Person继承了object
    def __init__(self,name,age,score):
        Person.__init__(self,name,age) # 必须显式调用父类初始化方法,不然解释器不会自己去调用
        self.score = score
        print("分数是:",self.score)

print(Student.mro())
s = Student("Jack",24,98)
s.say_age()
print(s.name)
print(s.score)
print(s._Person__age) # 读取父类的私有属性

零基础小白的Python学习之路(六)面向对象编程

类成员的继承和重写

  1. 成员继承:子类继承了父类除构造方法之外的所有成员
  2. 方法重写:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写

【测试】

class Person:
    def __init__(self,name,age):
        self.name = name
        self.__age = age # 私有

    def say_age(self):
        print(self.name,"的年龄是:",self.__age)

    def say_intr(self):
        print("我的名字是{0}".format(self.name)) # 这个被子类的覆盖(重写)了

class Student(Person): # Student继承了Person,Person继承了object
    def __init__(self,name,age,score):
        Person.__init__(self,name,age) # 必须显式调用父类初始化方法,不然解释器不会自己去调用
        self.score = score
        print("分数是:",self.score)
    
    def say_intr(self):
        '''重写了父类的方法'''
        print("我是{0},呵呵".format(self.name))

s = Student("Kevin",24,90)
s.say_age()
s.say_intr()

零基础小白的Python学习之路(六)面向对象编程

查看类的继承层次结构

通过类的方法mro()或者类的属性__mro __可以输出这个类的继承层次结构

class A:
    pass
class B(A):
    pass
class C(B):
    pass

print(C.mro())
print(C.__mro__)

零基础小白的Python学习之路(六)面向对象编程

object根类

object是所有类的父类,所有类都具有object类的属性和方法

dir()查看对象属性

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

    def say_age(self):
        print(self.namne,"的年龄是:",self.age)

obj = object()
print(dir(obj))

s2 = Person("Stefan",24)
print(dir(s2))

【执行结果】
[‘class’, ‘delattr’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’]

[‘class’, ‘delattr’, ‘dict’, ‘dir’, ‘doc’, ‘eq’, ‘format’, ‘ge’, ‘getattribute’, ‘gt’, ‘hash’, ‘init’, ‘init_subclass’, ‘le’, ‘lt’, ‘module’, ‘ne’, ‘new’, ‘reduce’, ‘reduce_ex’, ‘repr’, ‘setattr’, ‘sizeof’, ‘str’, ‘subclasshook’, ‘weakref’, ‘age’, ‘name’, ‘say_age’]

【注】

  1. Person对象增加了6个属性:
     __dict __,__module __,__weakref __,age, name , say_age
  2. Person类作为object的子类,显然包含了所有的属性
  3. 打印age,name,say_age,发现say_age虽然是方法,实际上也是属性。只不过,这个属性的类型是“method”而已
class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def say_age(self):
        print(self.name,"的年龄是:",self.age)

obj = object()
print(dir(obj))

s2 = Person("Stefan",24)
print(dir(s2))
print(type(s2.age))
print(type(s2.name))
print(type(s2.say_age))

零基础小白的Python学习之路(六)面向对象编程

重写__str__方法

object有一个__str __()方法,用于返回一个对于“对象的描述”,对应于内置函数str();经常用于print()方法,帮助我们查看对象的信息;__str __()可以重写

【原来】

class Person: # 默认继承object
    def __init__(self,name,age):
        self.name = name
        self.age = age


p = Person("Jim",24)
print(p)

零基础小白的Python学习之路(六)面向对象编程
【重写后】

class Person: # 默认继承object
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def __str__(self): # 重写
        return "名字是:{0}".format(self.name)


p = Person("Jim",24)
print(p)

零基础小白的Python学习之路(六)面向对象编程

多重继承

Python支持多重继承,一个子类可以有多个“直接父类”。这样,就具备了“多个父类”的特点。但是这样会被“类的整体层次”搞得异常复杂,尽量避免使用。

class A:
    def aa(self):
        print("aa")

class B:
    def bb(self):
        print("bb")
        
class C(B,A):
    def cc(self):
        print("cc")

c = C()
c.cc()
c.bb()
c.aa()

零基础小白的Python学习之路(六)面向对象编程

MRO()

Python支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将“从左向右”按顺序搜索
MRO(Method Resolution Order):方法解析顺序。
我们可以通过mro()方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的。

super()获得父类定义

在子类中,如果想要获得父类的方法时,我们可以通过super()来做。
super()代表父类的定义(代码),不是父类对象

# 测试super() 代表父类定义
class A:
    def say(self):
        print("A:",self)

class B(A):
    def say(self):
        # A.say(self)
        super().say()
        print("B:",self)

B().say()

零基础小白的Python学习之路(六)面向对象编程

【多态】

多态(polymorphism),指同一个方法调用由于对象不同可能会产生不同的行为。

【注】

  1. 多态是方法的多态,属性没有多态
  2. 多态的存在有2个必要条件:继承方法重写

【测试】

class Animal:
    def shout(self):
        print("动物叫。。。")

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

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

class Sheep(Animal):
    def shout(self):
        print("咩咩咩")

def animal_shout(m):
    if isinstance(m,Animal):
        m.shout() # 方法多态,一个方法调用,根据不同的对象调用不同的方法
    else:
        print("它不叫")

animal_shout(Animal())
animal_shout(Dog())
animal_shout(Cat())
animal_shout(Sheep())

零基础小白的Python学习之路(六)面向对象编程

对象的进化

随着编程面临的问题越来越复杂,编程语言本身也在进化。从主要处理简单数据开始,随着数据变多进化为“数组”;数据类型变复杂,进化出了“结构体”;处理数据的方式和逻辑变复杂,进化出了“对象”。

类的定义

通过类定义数据类型的属性(数据)和方法(行为),也就是说,“类将行为和状态打包在一起”
零基础小白的Python学习之路(六)面向对象编程
对象是类的具体实体,一般称为“类的实例”,就好比一个糕点模具,对象根据这个模具制造出各种甜点。
从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态)。更具体一点,“方法代码是共享的,属性数据不共享。”
零基础小白的Python学习之路(六)面向对象编程
定义类的语法格式:

class 类名:
 类体(就是一些属性和方法等)

【注】

  1. 类名必须符合“标识符”规则(一般,首字母大写,多个单词—>驼峰原则)
  2. 类体中我们可以定义属性和方法
  3. 属性用来描述数据,方法(即函数)用来描述这些数据相关的操作

【例】

class Student:

    def __init__(self,name,score): # 构造函数 self必须位于第一个参数 self对象本身
        self.name = name
        self.score = score

    def say_score(self): # self必须位于第一个参数
        print("{0}的分数是:{1}".format(self.name,self.score))

s1 = Student("Stefan",96) # 调用构造方法(构造器)初始化一个对象
s1.say_score()

零基础小白的Python学习之路(六)面向对象编程
零基础小白的Python学习之路(六)面向对象编程

构造函数__init__()

类是抽象的,也称为“对象的模板”。我们需要通过类这个模板,创建类的实例对象,然后才能使用类定义的功能

进一步展开Python对象包含的三个部分:

  • id(identity 识别码)
  • type(对象类型)
  • value(对象的值)
    -属性(attribute)
    -方法(method)

创建对象,我们需要定义构造函数__init__()方法
构造方法用于执行“实例对象的初始化工作”,即对象创建后,初始化当前对象的相关属性,无返回值

【注】

  1. 名称固定:__ init __
  2. 第一个参数固定,必须为:self(self:刚刚创建好的实例对象)
  3. 构造函数通常用来初始化实例对象的实例属性
    def __init__(self,name,score): # 实例属性
        self.name = name
        self.score = score
  1. 通过“类名(参数列表)”来调用构造函数。调用后,将创建好的对象返回给相应的变量
  2. __ init __()方法初始化创建好的对象(初始化:给实例属性赋值)
  3. __ new __()方法:用于创建对象,但我们一般无需重定义该方法

【注】
Python中的self相当于 C++ 中的self指针,JAVA 和 C# 中的this关键字。Python中,self必须为构造函数的第一个参数,名字可以任意修改,但一般遵守惯例,都叫做self。

实例属性

实例属性是从属于实例对象的属性,也称为“实例变量”。
几个要点:

  1. 实例属性一般在__ init __()方法中通过如下代码定义:
     self.实例属性名 = 初始值
  2. 在本类的其他实例方法中,也是通过self进行访问:
     self.实例属性名
  3. 创建实例对象后,通过实例对象访问:
     obj01 = 类名()  # 创建对象,调用__ init __()初始化属性
     obj01.实例属性名 = 值  # 可以给已有属性赋值,也可以新加属性
class Student:

    def __init__(self,name,score): # self必须位于第一个参数 self对象本身,
        self.name = name
        self.score = score

    def say_score(self): # self必须位于第一个参数
        print("{0}的分数是:{1}".format(self.name,self.score))

s1 = Student("Stefan",96) # 调用构造方法初始化一个对象
s1.say_score()

s1.age = 24
s1.salary = 30000
print(s1.salary)

s2 = Student("XXC",23)
print(s2.name)

零基础小白的Python学习之路(六)面向对象编程
零基础小白的Python学习之路(六)面向对象编程
【内存分析】
零基础小白的Python学习之路(六)面向对象编程

实例方法

实例方法是从属于实例对象的方法
基本格式:

def 方法名(self [, 形参列表])
 函数体

方法的调用格式如下:

 对象.方法名([实参列表])

【注】

  1. 定义实例方法时,第一个参数必须为self。和前面一样,self指当前的实例对象
  2. 调用实例方法时,不需要也不能给self传参。self由解释器自动传参。

【注】区别函数和方法

  1. 都是用来完成一个功能的语句块,本质是一样的。
  2. 方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点。
  3. 直观上看,方法定义时需要传递self,函数不需要。

【例】

s2 = Student("XXC",77)
s2.say_score()
Student.say_score(s2) # 解释器实际的调用 和上句输出结果是一样的

零基础小白的Python学习之路(六)面向对象编程
【内存分析】
零基础小白的Python学习之路(六)面向对象编程
【其他操作】:

  1. dir(obj):可以获得对象的所有属性、方法
  2. obj.__ dict __:对象的属性字典
  3. pass:空语句
  4. isinstance(对象 , 类型):判断“对象”是不是“指定类型”
print(dir(s2)) # 所有的
print(s2.__dict__) # 只是我们定义的

class Man:
    pass

print(isinstance(s2,Student))

零基础小白的Python学习之路(六)面向对象编程

类对象

当解释器执行class语句时,就会创建一个类对象

【测试类对象】

class Student:
    pass

print(type(Student))
print(id(Student))

Stu2 = Student
s1 = Stu2()
print(s1)

零基础小白的Python学习之路(六)面向对象编程

类属性和类方法

类属性

从属于“类对象”的属性,也称为“类变量”。由于类属性从属于类对象,可以被所有实例对象共享

类属性定义方式:

class 类名:
 类变量名 = 初始值

在类中或者类的外面,我们可以通过:“类名.类变量名”来读写

class Student:
    company = "STC" # 类属性
    count = 0 # 类属性

    def __init__(self,name,score):
        self.name = name # 实例属性
        self.score = score
        Student.count = Student.count + 1

    def say_score(self): # 实例方法
        print("My Company is:",Student.company)
        print(self.name,'的分数是:',self.score)

s1 = Student('Stefan',84) # s1是实例对象,自动调用__init__()方法
s1.say_score()
print(id(s1))
s2 = Student('Jack',90)
s3 = Student('Jim',89)
print('一共创建{0}个Student对象'.format(Student.count))

零基础小白的Python学习之路(六)面向对象编程
【内存分析】
零基础小白的Python学习之路(六)面向对象编程

类方法

从属于“类对象”的方法。类方法通过装饰器@classmethod来定义,格式如下:

@classmethod
def 类方法名(cls [, 形参列表]):
 函数体

【注】

  1. @classmethod 必须位于方法上面第一行
  2. 第一个cls必须有;cls(指“类对象”本身)
  3. 调用类方法格式:“类名.类方法名(参数列表)”。参数列表中,不需要也不能给cls传值
  4. 类方法中访问实例属性和实例方法会导致错误
  5. 子类继承父类方法时,传入cls是子类对象,而非父类对象

【测试】

class Student:
    company = "STC"
    
    @classmethod # 只属于类
    def printCompany(cls):
        print(cls.company)
        
Student.printCompany()

零基础小白的Python学习之路(六)面向对象编程

静态方法

Python中允许定义与“类对象”无关的方法,称为“静态方法”
“静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空间里面”,需要通过“类调用”。

静态方法通过装饰器@staticmethod来定义,格式:

@staticmethod
def 静态方法名([形参列表]):
 函数体

【注】

  1. @staticmethod 必须位于方法上面一行
  2. 调用静态方法格式:“类名.静态方法名(参数列表)
  3. 静态方法中访问实例属性和实例方法会导致错误(调不了self)
class Student2:
    company = "STC"

    @staticmethod
    def add(a,b): # 静态方法
        print("{0} + {1} = {2}".format(a,b,(a+b)))
        return a+b

Student2.add(20,30)

__ del __方法(析构函数)和垃圾回收机制

__ del__方法称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象占用的资源,例如:打开的文件资源、网络连接等。
Python实现自动的垃圾回收当对象没有被引用时(引用计数为0),由垃圾回收器调用__ del __方法
我们也可以通过del语句删除对象,从而保证调用__del __方法
系统会自动提供__del __方法,一般不需要自定义析构方法

【垃圾回收机制】
零基础小白的Python学习之路(六)面向对象编程
【测试】

# 析构函数
class Person:

    def __del__(self):
        print("销毁对象{0}".format(self))

p1 = Person()
p2 = Person()
del p2
print("程序结束")
print(p1)
print(p2)

零基础小白的Python学习之路(六)面向对象编程
【注】p2已经被销毁了,所以会报错

__call __方法和可调用对象

定义了__call __方法的对象,称为“可调用对象”,即该对象可以像函数一样被调用

【测试】

# 测试可调用方法__call__()
class SalaryAccount:
    '''工资计算'''
    def __call__(self, salary):
        print('算工资')
        yearSalary = salary * 12
        # monthsalary = salary
        daySalary = salary // 22.5
        hourSalary = daySalary // 8
        return dict(yearSalary = yearSalary,monthSalary = salary,daySalary = daySalary,hourSalary = hourSalary)

s = SalaryAccount()
print(s(30000))

零基础小白的Python学习之路(六)面向对象编程

Python中理解的“方法重载”

在C++等其他语言中,可以定义多个重名的方法,只要保证方法签名唯一即可。
方法签名包含3个部分:方法名、参数数量、参数类型
Python中,方法的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。因此,Python中是没有方法的重载的。
定义一个方法即可有多种调用方式,相当于实现了其他语言中的方法的重载

注意,如果我们在类体中定义了多个重名的方法,只有最后一个方法有效!

总结起来,Python是没有重载的

方法的动态性

Python是动态语言,我们可以动态地为类添加新的方法,或者动态地修改类已有的方法

# 测试动态性
class Person:
    def work(self):
        print("Hard Work!")

def play_game(s):
    print("{0} is playing game!".format(s))

def work2(s):
    print("Study hard")
Person.play = play_game
p = Person()
p.work()
p.play() # Person.play(p)
Person.work = work2
p.work()

零基础小白的Python学习之路(六)面向对象编程

私有属性和私有方法(实现封装)

Python中对类的成员并没有严格的访问控制限制。
【注】

  1. 通常我们约定,两个下划线开头的属性是私有的(private),其他为公共的(public)。
  2. 类内部可以访问私有属性(方法)。
  3. 内外部不能直接访问私有属性(方法)。
  4. 内外部可以通过“_ 类名__私有属性(方法)名”(注意是前面一个下划线,后面两个下划线)访问私有属性(方法)。

【注】方法本质上也是属性,只不过是可以通过()执行而已。所以,此处讲的私有属性和公有属性,也同时讲解了私有方法和公有方法的用法。

【测试1】

# 测试私有属性
class Employee:

    def __init__(self,name,age):
        self.name = name # 公有
        self.__age = age # 私有

e = Employee("Stefan",24)
print(e.name)
print(e._Employee__age)
print(dir(e))

零基础小白的Python学习之路(六)面向对象编程
【测试2】

# 测试私有方法
class Employee:

    __company = "BTS"

    def __init__(self,name,age):
        self.name = name # 公有
        self.__age = age # 私有

    def __work(self): # 私有方法
        print("Work hard makes perfect")
        print("年龄:{0}".format(self.__age))
        print(Employee.__company)

e = Employee("Stefan",24)
print(e.name)
print(e._Employee__age)
e._Employee__work()
print(e._Employee__company)
print(Employee._Employee__company)
print(dir(e))

零基础小白的Python学习之路(六)面向对象编程

@property装饰器

@property 可以将一个方法的调用方式变成“属性调用”。

# 测试@property(属性)
class Employee:

    @property
    def salary(self):
        print("salary is running...")
        return 10000

emp1 = Employee()
# emp1.salary()
print(emp1.salary) # 属性调用
emp1.salary = 20000 # 允许调用,但不允许设置,会报错

零基础小白的Python学习之路(六)面向对象编程
【测试1】不用装饰器自己定义get、set方法

class Employee:

    def __init__(self,name,salary):
        self.__name = name
        self.__salary = salary
    def get_salary(self):
        return self.__salary
    def set_salary(self,salary):
        if 1000 < salary < 50000:
            self.__salary = salary
        else:
            print("录入错误!薪水在1000~50000范围内")
emp1 = Employee("Jack",30000)
print(emp1.get_salary())
emp1.set_salary(-2000)
print(emp1.get_salary())

零基础小白的Python学习之路(六)面向对象编程
【测试2】采用@property 装饰器

# 测试 @property(属性)
class Employee:

    def __init__(self,name,salary):
        self.__name = name
        self.__salary = salary

    @property
    def salary(self):
        return self.__salary

    @property
    def name(self):
        return self.__name

    @salary.setter
    def salary(self,salary):
        if 1000 < salary < 50000:
            self.__salary = salary
        else:
            print("录入错误!薪水在1000~50000范围内")

    @name.setter
    def name(self,name):
        if len(name) > 4:
            print("录入错误!姓名越界")
        else:
            self.__name = name

emp1 = Employee("Jack",30000)
print(emp1.name,emp1.salary)
emp1.name = "KKJHGGGJH"
emp1.salary = 2990
print(emp1.name,emp1.salary)

零基础小白的Python学习之路(六)面向对象编程

特殊方法和运算符重载

Python的运算符实际上是通过调用对象的特殊方法实现的
比如:

a = 20
b = 30
c = a + b
d = a.__add__(b)
print("c = ",c)
print("d = ",d)

零基础小白的Python学习之路(六)面向对象编程
【常见特殊方法】
零基础小白的Python学习之路(六)面向对象编程
【运算符对应方法】
零基础小白的Python学习之路(六)面向对象编程
【测试】

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

    def __str__(self):
        '''将对象转化成一个字符串,一般用于print方法'''
        return "名字是{0},年龄是{1}".format(self.name,self.__age)

    def __add__(self, other):
        if isinstance(other,Person):
            return "{0}--{1}".format(self.name,other.name)
        else:
            return "不是同类对象,不能相加!"

    def __mul__(self, other):
        if isinstance(other,int):
            return self.name * other
        else:
            return "不是同类对象,不能相乘!"

p1 = Person("Stefan",24)
p2 = Person("Jack",18)
x = p1 + p2 # __add__重载
print(x)

print(p1.name * 3) # __mul__重载
print(p2._Person__age * 3)

print(p1) # print重载
print(p2)

零基础小白的Python学习之路(六)面向对象编程

特殊属性

Python对象中包含了很多双下划线开始和结束的属性,这些是特殊属性,有特殊用法。

【常见的特殊属性】
零基础小白的Python学习之路(六)面向对象编程
【测试】

class A:
    def aa(self):
        print("aa")

    def say(self):
        print("say aaa")


class B:
    def bb(self):
        print("bb")

    def say(self):
        print("say bbb")


class C(B, A):
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def cc(self):
        print("cc")


c = C("Kim",45)
print(C.mro()) # 类层次结构
c.say()
print(dir(c))
print(c.__dict__) # 对象的属性字典
print(c.__class__) # 对象所属的类
print(C.__bases__) # 类的基类元组(多继承)
print(C.__base__) # 类的基类
print(A.__subclasses__()) # 子类列表
print(C.__mro__) # 类层次结构

零基础小白的Python学习之路(六)面向对象编程

对象的浅拷贝和深拷贝

【测试】

import copy
class Phone:
    def __init__(self,cpu,screen):
        self.cpu = cpu
        self.screen = screen

class CPU:
    def calculate(self):
        print("计算")
        print("cpu对象",self)

class Screen:
    def show(self):
        print("显示出来!")
        print("screen对象",self)

# 测试变量赋值
print("测试变量赋值")
c1 = CPU()
c2 = c1
print(c1)
print(c2)

# 测试浅拷贝
print("测试浅拷贝")
s1 = Screen()
m1 = Phone(c1,s1)
m2 = copy.copy(m1)

print(m1,m1.cpu,m1.screen)
print(m2,m2.cpu,m2.screen)

# 测试深拷贝
print("测试深拷贝")
m3 = copy.deepcopy(m1)
print(m1,m1.cpu,m1.screen)
print(m3,m3.cpu,m3.screen)

零基础小白的Python学习之路(六)面向对象编程

组合

“is-a”关系,我们可以使用“继承”,从而实现子类拥有的父类的方法和属性。(前者 is a 后者–前者继承后者类)

“has-a”关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性。(前者 has a 后者)

【测试1】

# 继承
class A1:
    def say_a1(self):
        print("a1,a1,a1")

class B1(A1):
    pass

b1 = B1()
b1.say_a1()

# 组合
class A2:
    def say_a2(self):
        print("a2,a2,a2")

class B2:
    def __init__(self,a):
        self.a = a

a2 = A2()
b2 = B2(a2)
b2.a.say_a2()
# b2拥有a2

零基础小白的Python学习之路(六)面向对象编程
【测试2】

class Phone:
    def __init__(self,cpu,screen):
        self.cpu = cpu
        self.screen = screen

class CPU:
    def calculate(self):
        print("计算")
        print("cpu对象",self)

class Screen:
    def show(self):
        print("显示出来!")
        print("screen对象",self)

m = Phone(CPU(),Screen())
m.cpu.calculate()
m.screen.show()

零基础小白的Python学习之路(六)面向对象编程

设计模式

设计模式是面向对象语言特有的内容
比较流行的有:GOF23种设计模式
GOF(Goup Of Four)

工厂模式

工厂模式实现了创建者调用者的分离,使用专门的工厂类将选择实现类,创建对象进行统一的管理和控制
【测试】

# 测试工厂模式
class CarFactory:
    def create_car(self,brand):
        if brand == "奔驰":
            return Benz()
        elif brand == "宝马":
            return BMW()
        elif brand == "比亚迪":
            return BYD()
        else:
            return "未知品牌,无法创建!"
class Benz:
    pass

class BMW:
    pass

class BYD:
    pass

factory = CarFactory()
c1 = factory.create_car("奔驰")
c2 = factory.create_car("比亚迪")
print(c1)
print(c2)

零基础小白的Python学习之路(六)面向对象编程

单例模式

单例模式(Singleton Pattern)核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点
单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如,读取配置文件,产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销

【测试1】

# 测试单例模式
class MySingleton:
    __obj = None
    __init_flag = True
    def __new__(cls, *args, **kwargs):
        if cls.__obj == None:
            cls.__obj = object.__new__(cls)

        return cls.__obj

    def __init__(self,name):
        if MySingleton.__init_flag:
            print("init... ...")
            self.name = name
            MySingleton.__init_flag = False

a = MySingleton("aa")
b = MySingleton("bb")
c = MySingleton("cc")
print(a)
print(b)
print(c)

零基础小白的Python学习之路(六)面向对象编程

本文地址:https://blog.csdn.net/qq_35495464/article/details/106982131

相关标签: python