0
点赞
收藏
分享

微信扫一扫

AI算法工程师 | 02人工智能基础-Python基础(七)面向对象编程

上善若水山西太原 2022-05-01 阅读 13

文章目录

Python基础语法 之 面向对象编程

在各编程语言中,Java 是面向对象语言,C 是面向过程语言,而 Python 既执行面向过程编程,也支持面向对象编程。

一、面向过程和面向对象思想

概述

  • 面向过程思想思考问题时,首先思考 “怎么按步骤实现?” 并将步骤对应成方法,一步一步,最终完成。( 执行者思维 )
  • 面向对象思想更契合人的思维模式。首先思考的是“怎么设计这个事物?”,而不是自己一步一步的去操作实现。( 设计者思维 )

理解-面向对象思想

乔布斯是这样解释面向对象编程的:

例子分析:
例子-面向对象
总的来说,就是:“你”只需要把事情交代给“我”,“我”来实现该需求,等事情完成,“你”看到的就是一个完成后的结果。至少细节,“你”不关心,也无需知道,原本复杂的事情,对“你”来说,变得简单了不少。

区别

  • 面向过程(强调步骤):当需要实现一个功能的时候,每一个具体的步骤都要亲力亲为(过程),详细处理每一个细节

  • 面向对象(强调对象):当需要实现一个功能的时候,不关心具体的步骤,而是找一个已经具有该功能的人(对象),来帮我做事

二、类和对象

类和对象的概念
类和对象导图 by艾伦

实例:我们根据抽象的设计图,设计出具体的各种车,这个过程就是——实例化具体的对象
类和对象

类的定义

  • Python 使用 class 关键字定义一个类,类名的首字母一般要大写
  • 当程序员需要创建的类型不能用简单类型(如:int、float 这些也是类)来表示时,则需要定义类,然后利用定义的类创建对象
  • 类把需要使用的变量和方法组合在一起,这种方法称为封装

语法格式:

class 类名(父类名):
	类体

# 注:圆括号中的父类名就是要继承的类,如果没写默认继承的类是 object

示例:定义一个 Student 类

class Student:   #  等同于 class Student(object):
    pass  # 当定义了类后,里面什么内容都不写时,可使用 pass 作为占位符,从而不会报错。

# 说明:pass 为空语句,就是表示什么都不做,只是作为一个占位符存在。
# 对于一个类来说,一般有三种常见的成员:属性、方法、构造器。这三种成员都可以定义零个或多个。

创建对象

  • 创建对象的过程称为实例化。创建对象,需要定义构造方法__init__()(两个下划线开头和两个下划线结尾)
  • 构造方法用于执行 “实例对象的初始化工作”,即对象创建后,初始化当前对象的相关属性,无返回值。

示例:定义学生类,并创建对象

# 定义类
class Student(object):
    def __init__(self, temp_name, temp_age):  # 构造方法
        self.name = temp_name  # 实例属性
        self.age = temp_age 

# 知识补充-构造方法__init__():
# 1. 方法的名称是固定的,必须为:__init__()
# 2. 第一个参数固定,必须为:self。self 指的就是刚刚创建好的实例对象。
# 3. 构造函数通常用来初始化实例对象的实例属性,如:temp_name、temp_age
# 4. 通过“类名(参数列表)”来调用构造函数。调用后,将创建好的对象返回给相应的变量。 例如:s1 = Student('张三', 18)
# 5. 
# - 如果不定义__init__方法:
#		系统会提供一个默认的__init__方法。
#		如果定义了带参的__init__方法,系统不创建默认的__init__方法。
# - 如果定义了__init__方法:
#		在创建实例的时候,就不能传入空的参数了,必须传入与__init__方法匹配的参数,
#		但 self 不需要传,Python 解释器自己会把实例变量传进去。
# 创建对象
s1 = Student("张三", 18)  # s1 是实例对象,自动调用__init__()方法(参数匹配)
print(s1.name)
print(s1.age)

s2 = Student() # 会报错 (参数不匹配)
张三
18
------------------------------------------------------------
TypeError                    Traceback (most recent call last)
Input In [10], in <cell line: 4>()
      2 print(s1.name)
      3 print(s1.age)
----> 4 s2 = Student()

TypeError: __init__() missing 2 required positional arguments: 'temp_name' and 'temp_age'

三、属性和方法

每一个类都具有自己的属性和方法。属性和方法是面向对象程序设计所独有的概念。属性是类所封装的数据,而方法则是类对数据进行的操作。

实例属性和方法

实例属性

  • 实例属性是从属于实例对象的属性,也称为 “实例变量”
  • 实例属性一般定义在__init__()(两个下划线开头和两个下划线结尾)方法中

语法格式:

self.实例属性名 = 初始值

# 在本类的其他实例方法中,可以通过 self 进行访问:————————
self.实例属性名

# 创建实例对象后,通过实例对象访问:————————
obj01 = 类名() # 创建对象,调用__init__()初始化属性
obj01.实例属性名 =# 可以给已有属性赋值,也可以新加属性

实例方法

  • 实例方法是从属于实例对象的方法

语法格式:

# 实例方法的定义
方法名(self [, 形参列表]):
	方法体
	
# 方法的调用格式如下:————————
对象.方法名([实参列表])

示例

class Student(object):
    
    def __init__(self, temp_name, temp_age):   
        self.name = temp_name  # 实例属性
        self.age = temp_age
        
    def change_age(self, new_age): # 实例方法
        self.age = new_age  # 在实例方法中通过 self 访问实例属性 或 赋值
        
    def get_name(self): # 实例方法
        return self.name  # 在实例方法中访问实例属性


s1 = Student("张三", 18)  # s1 是实例对象,自动调用__init__()方法

print(s1.name) # 创建实例对象后,通过实例对象访问 实例属性
print(s1.age)

print(s1.get_name()) # 调用实例方法 

s1.change_age(20)  # 调用实例方法
print(s1.age)
张三
18
张三
20

类属性和方法

类属性

  • 直接在 class 中定义的属性就是类属性,实际上就是类内部的变量

语法格式:

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

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

示例:

class Student(object):
    company = "地中海"   # 类属性
    count = 0  # 类属性
    
    def __init__(self, temp_name, temp_age): 
        self.name = temp_name # 实例属性
        self.age = temp_age
        Student.count += 1   # 通过“类名.类变量名”来读写 类属性
        
    def say_score(self): # 实例方法
        print("我的公司是:", Student.company)


s1 = Student("张三", 18)  # s1 是实例对象,自动调用__init__()方法
s1.say_score()
s2 = Student("李四", 19)

print(Student.company)  # 通过“类名.类变量名”来读写类属性
print(Student.count)
我的公司是: 地中海
地中海
2

图解 - 实例对象和类对象创建过程:

  1. 在执行 class 类时,解释器会加载类的所有内容,先创建一个 Student 类的类型对象(type), 类属性和方法会被加载到类对象中;
  2. 调用构造器 __ init __() 把对象构造起来,对象中包括实例属性和方法。其中,两个实例属性被传入,而方法从类对象中获得;
  3. 由于多了一个 s2 实例对象,则重复 步骤2 ——多一个构造器把对象构造起来的过程。需要注意的是:不管创建了多少个实例对象,类对象依旧只有一个。
    图解 - 实例对象和类对象创建过程

类方法

  • 类方法是从属于 “类对象” 的方法( 当解释器执行 class 语句时,就会创建一个类对象 )
  • 类方法通过装饰器 @classmethod 来定义

语法格式:

@classmethod   # @classmethod 必须位于方法上面一行
def 类方法名(cls[,形参列表])# cls 必须有,指 “类对象” 本身
	函数体

# 说明:
# 1. 调用类方法格式:“类名.类方法名(参数列表)”。 参数列表中,不需要也不能给cls 传值。
# 2. 类方法中访问实例属性和实例方法会导致错误。
# 3. 子类继承父类方法时,传入 cls 是子类对象,而非父类对象。

示例:

class Student(object):
    company = "地中海"   # 类属性
    
    @classmethod    # 类方法
    def printCompany(cls):
        print("我的公司是:", Student.company)
        
Student.printCompany()
我的公司是: 地中海

内置方法与运算符重载

内置方法

  • Python 类定义了一些专用的方法,这些专用的方法丰富了程序设计的功能,用于不同的应用场合。

常见内置方法:(以下方法名均以两个下划线开头和两个下划线结尾)
常见内置方法
示例:

class Student(object):
 
    def __init__(self, temp_name, temp_age): # 构造方法,初始化对象
        self.name = temp_name # 实例属性
        self.age = temp_age

    def __str__(self):  # 重写 toString 方法,返回自定义的字符串
        return "student name is %s, student age is %s" % (self.name, self.age)
    
    def __call__(self, a): # 把实例对象作为函数调用,该方法也可以传参
        if self.age + a > 18: 
            return True 
        else:  
            return False
        
        
s1 = Student("张三", 18)  # s1 是实例对象,自动调用__init__()方法
s2 = Student("李四", 17)

print(s1)  # 输出 s1 对象时候,调用__str__()方法
s1(1), s2(1) # 可以像调用函数一样调用对象的__call__方法
student name is 张三, student age is 18
(True, False)

运算符重载

  • 使用 Python 里的运算符实际上是调用了对象的方法,例如:+ 运算符是类里提供的__add__方法,当调用 + 实现加法运算的时候,实际上是调用了__add__方法。
  • 运算符的重载实际上是对运算符对应的专有方法的重载。

运算符与类专有方法对照表:
对照表
示例:

  • 函数__add__ ()的使用
a = 20
b = 30
c = a+b
d = a.__add__(b)
print("c=",c)
print("d=",d)
c= 50
d= 50
  • 运算符的重载
class Student(object):
 
    def __init__(self, temp_name, temp_age): # 构造方法,初始化对象
        self.name = temp_name 
        self.age = temp_age

    def __add__(self, other):  # 重载
        return self.age + other.age
        
s1 = Student("张三", 18)  
s2 = Student("李四", 17)

s2 + s1  # 调用 __add__()方法
35

知识补充-方法没有重载

  • Python 中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。
  • 因此,Python 中是没有方法的重载的。定义一个方法即可有多种调用方式,相当于实现了其他语言中的方法的重载。
  • 如果在类体中定义了多个重名的方法,只有最后一个方法有效。

建议:不要使用重名的方法,Python 中方法没有重载。

示例:

#Python 中没有方法的重载。定义多个同名方法,只有最后一个有效
class Person:

    def say_hi(self):
        print("hello")
        
    def say_hi(self,name,age):
        print("姓名:{0},年龄:{1}".format(name,age))
        
p1 = Person()
p1.say_hi("张三",23)
p1.say_hi() #不带参,报错
姓名:张三,年龄:23
--------------------------------------------------------------
TypeError                   Traceback (most recent call last)
Input In [43], in <cell line: 10>()
      8 p1 = Person()
      9 p1.say_hi("张三",23)
---> 10 p1.say_hi()

TypeError: say_hi() missing 2 required positional arguments: 'name' and 'age'

私有属性和私有方法

Python 对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。

说明:

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

示例:

class Student(object):
    
    __company = "花果山" # 私有类属性(在类外部访问不到)
    
    def __init__(self, temp_name, temp_age): 
        self.name = temp_name 
        self.__age = temp_age # 私有实例属性
        
    def say_company(self):
        print("公司是:",Student.__company) # 类内部可以直接访问私有属性
        print(self.name,"的年龄是:",self.__age)
        self.__work() # 类内部可以直接访问私有方法
      
    def __work(self): # 私有实例方法
        print("又是工作的一天!")
    
    
s1 = Student("张三", 18) 
print(s1.name)
s1.say_company()

print(s1._Student__age) # 通过该方式可访问到私有属性
print(p1.__age) # 直接访问私有属性,报错
p1.__work() # 直接访问私有方法,报错
张三
公司是: 花果山
张三 的年龄是: 18
又是工作的一天!
18
--------------------------------------------------------
AttributeError           Traceback (most recent call last)
Input In [49], in <cell line: 23>()
     20 s1.say_company()
     22 print(s1._Student__age) # 通过该方式可访问到私有属性
---> 23 print(p1.__age) # 直接访问私有属性,报错
     24 p1.__work()

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

扩 - isinstance 函数

isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。

语法格式:

isinstance(object, classinfo)

# 说明:
# 第一个参数(object)为实例对象,
# 第二个参数(type)可以是直接或间接类名、基本类型或者由它们组成的元组。
# 其返回值为布尔型(True or Flase)。
# - 若对象的类型与参数二的类型相同则返回 True;
# - 若参数二为一个元组,则若对象类型与元组中类型名之一相同即返回 True。

示例:

isinstance(s1, Student)  # 接着上一个示例的代码,判断 s1 是否为 Student 类型
True

四、面向对象三大特征

面向对象三大特征:封装(隐藏)、继承、多态
面向对象三大特性
1. 封装(隐藏)

隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露“相关调用方法”。

通过“私有属性、私有方法”的方式,可实现“封装”。Python 追求简洁的语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。

2. 继承

继承可以让子类具有父类的特性,提高了代码的重用性。

从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。

3. 多态

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

五、类的继承

继承的实现

继承的概念:

  • 所谓类的继承,就是一个类(子类)从另外一个类(父类)获得了所有的成员。父类的成员可以在子类中使用。
  • 继承是面向对象程序设计的重要特征,也是实现“代码复用”的重要手段。
  • 如果一个新类(称为“子类或派生类”)继承自一个设计好的类,就直接具备了已有类(称为“父类或基类”)的特征,可降低工作的难度。

继承的语法格式:

class 子类类名(父类):
	类体

# 如果在类定义中没有指定父类,则默认父类是 object 类。
# 也就是说,object 是所有类的父类。

如果在子类中需要父类的构造方法就需要显示的调用父类的构造方法,调用格式如下:

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

示例:

  • 继承实现
class Person:    
    def sing(self):
        print("人在唱歌")
        
class Student(Person):
    def dance(self):
        print("人在跳舞")
        
class Teacher(Person):
    pass
    
s1 = Student()
s1.dance()
s1.sing()

t1 = Teacher()
t1.sing()
人在跳舞
人在唱歌
人在唱歌
  • 子类中显示调用父类的构造方法
class Person:  
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
    def sing(self):
        print("人在唱歌")
        
class Student(Person):
    def __init__(self,name,age,score):
        self.score = score
        # 子类并不会自动调用父类的__init__()构造方法,需要显式的调用
        Person.__init__(self, name, age)

s1 = Student("张三", 18, 100)
s1.name, s1.age, s1.score
('张三', 18, 100)

方法的重写

  • 子类继承了父类除构造方法之外的所有成员,如果父类中方法的功能不能满足子类的需求,子类可以重写父类的方法。

示例:

class Person:  
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
    def sing(self):
        print("人在唱歌")
        
class Student(Person):
    def __init__(self,name,age,score):
        self.score = score
        #子类并不会自动调用父类的__init__()构造方法,需要显式的调用
        Person.__init__(self, name, age)
    
    def dance(self):
        print("人在跳舞")
        
    #重写父类的方法    
    def sing(self, song):
        print("在唱%s"%song)
    
    
s1 = Student("张三", 18, 100)
s1.sing("黄河大合唱")
在唱黄河大合唱

检测继承关系

  • 可以通过 issubclass 函数去判断 B 是否是 A 的父类。

语法格式:

issubclass(sub,sup)

# 第 1 个参数是子类、第 2 个参数是父类。
# 如果第 1 个参数指定的类与第 2 个参数指定的类确实是继承关系,那么该函数返回 True,否则返回 False。

多继承

  • Python 支持多继承,而 Java 属于单继承;
  • 多继承:是指一个子类可以有多个“直接父类”。这样,就具备了“多个父类”的特点。

语法格式:

class 子类类名(父类 1,父类 2,父类 3):
	类体

# 要想为某一个类指定多个父类,需要在类名后面的圆括号中设置。
# 多个父类名之间用逗号(,)分隔

查看类的继承层次结构

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

示例:

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

print(C.mro()) # 从输出结果可看出:C继承B,B继承A,A继承object
[<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]

super()

  • 在子类中,如果想要获得父类的方法时,可以通过super()。

示例:

class A:
    def say(self):
        print("say AAA")
class B(A):
    def say(self):
        A.say(self) # 调用父类的 say 方法
        super().say() # 通过 super()调用父类的方法
        print("say BBB")

b = B()
b.say()
say AAA
say AAA
say BBB

—— 说明:本文内容基于python3.0

举报

相关推荐

0 条评论