• Python 面向对象(上)


    一. 什么是面向对象?

    1. 在了解面向对象之前,首先我们需要知道两个概念:
    (1)什么是函数?
    函数是对功能或动作的一种封装.
    函数的语法结构如下:

    def func(arg1):
        '''函数的内部有函数体'''
        print("这里是函数内部")
    
    func(arg2)

    上面的结构中, func是函数名, arg1是形参, 在函数的内部是函数体. 在定义了函数以后, 我们只需要在下面 用函数名接上一个小括号( func() ) 就可以调用函数了, 小括号内部的 arg2 表示实参.

    (2)什么是变量?
    在程序设计中, 变量是一个可以存储值的字母或名称, 它是程序运行过程中产生的值, 被用来提供给后面的程序使用.  如 a = 10, 我们把 10 这个值赋给变量 a.

    2. 面向过程与面向对象

    在了解了以上两个概念以后, 我们现在可以开始讨论什么是面向对象了.

    (1)从我们最初接触编程开始, 我们就使用一些关键词, 诸如 if, else, while 等等, 来帮助我们规范程序的逻辑, 按照我们的想法使程序运行下去. 实际上, 这种编程思想就是"面向过程", 我们可以用更加正式的语言来描述面向过程:

    面向过程: 一切以事物的流程为核心. 根据业务逻辑(即事物的发展过程, 动作的先后顺序)从上到下写垒代码, 其核心在于"流程(过程)".

    优点: 把一个事件步骤化,流程化, 程序编写相对简单.
    缺点: 可扩展性差(甚至可以说是极差).

    (2)于是我们现在引出一个概念 -- 面向对象.

    面向对象: 一切以对象为中心. 对象是属性和动作的集合体. 面向对象就是指对函数进行分类和封装.

    优点: 可扩展性强(体现在继承和多态上)
    缺点: 编程的复杂程度高于面向过程

    简单来说, 最开始接触编程时, 我们的思维,我们的思想是 专注于事物发展过程,动作先后顺序的. 然而, 面向对象却与之截然不同, 这种思维专注于事物这个对象本身, 处于这种思维之下, 可以认为世间万物皆是对象. 我们曾经关注的那些事物的动作和发展顺序, 在面向对象的思维中, 他们都是对象的属性, 于是, 无数包含着各种属性的对象就组成了我们的整个世界.

    二. 什么是面向对象编程?

    1. 类(Class)

    类(Class): 用来描述具有相同的属性和方法的对象的集合体. 它定义了该集合体中每个对象所共有的属性和方法. 对象是类的实例.

    class Foo:
        pass
    
    # class -- 关键字, 表示创建类
    # Foo -- 类名

    2. 实例化

    实例化:创建一个类的实例,类的具体对象

    class Foo:
    
        def __init__(self, property1, property2)
            self.property1 = property1
            self.property2 = property2
     
    obj = Foo("属性1", "属性2")
     
    # Foo() -- 类的实例化
    # obj = Foo() -- 把Foo实例化为obj, obj被称为实例对象(简称为"对象")
    # "属性1", "属性2" 分别被传给参数 property1, property2        

    3. 成员

    成员: 在类中定义的变量和方法都被称为成员.
    成员分类: 变量, 方法, 属性

    class Foo:
        class_variable = "这里是类变量"
    
        def __init__(self, property):
            """__init__用于初始化一个类"""
            self.property = property
            self.instance_variable = "实例变量"
            """这里的self.xxx都属于实例变量"""
    
        def instance_method(self):
            """实例方法必须有参数self"""
            print("这里是实例方法")
    
        @classmethod
        def class_method(cls):
            """类方法必须有参数cls,这里cls传递的是类名Foo"""
            obj = cls("普通属性")
            print("这里是类方法", obj)
    
        @staticmethod
        def static_method():
            """静态方法对参数没有固定要求,根据实际需要即可"""
            print("这里是静态方法")
    
        @property
        def dynamic_property(self):
            return "这里是动态属性"
    举例说明

    3.1 变量: 实例变量, 类变量(静态变量)

    (1)实例变量

    实例变量:定义在方法中的变量,只作用于当前实例的类.

    class Foo:
        
        def __init__(self, property):
            """__init__用于初始化一个类,它是构造方法"""
            self.property = property
            self.instance_variable = "实例变量"
            """这里的self.xxx都属于实例变量"""
    
    obj = Foo("普通属性")    # 类Foo实例化为对象obj
    s = obj.instance_variable     # 对象obj访问实例变量
    print(s)    # 输出结果为:  实例变量

    (2)类变量(静态变量)

    类变量:在Java中类变量也被称为静态变量. Python中类变量在整个实例化的对象中是公用的. 类变量定义在类中且在函数体之外. 约定俗成, 类变量通常不作为实例变量使用.

    class Foo:
        class_variable = "这里是类变量"
    
    obj = Foo()    # 实例化
    print(Foo.class_variable)    # 这里用类名Foo去访问类变量class_variable 
    
    # 输出结果:
    # 这里是类变量

    3.2 方法: 在类的内部定义的函数. 分为实例方法, 类方法和静态方法

    (1)实例方法

    定义: 第一个参数必须是实例对象, 该参数名一般约定为“self”, 通过它来传递实例的属性和方法(也可以传类的属性和方法)

    调用: 约定俗成由实例对象调用

    class Foo:
    
        def instance_method(self):
            """实例方法必须有参数self"""
            print("这里是实例方法")
    
    obj = Foo()                 # 类Foo实例化为对象obj
    obj.instance_method()    # 实例对象调用实例方法
    
    # 输出结果:
    # 这里是实例方法

    (2)类方法

    定义:使用装饰器@classmethod. 第一个参数必须是当前类对象, 该参数名一般约定为“cls”, 通过它来传递类的属性和方法(不能传实例对象的属性和方法)

    调用:实例对象和类对象都可以调用.

    class Foo:
    
        @classmethod
        def class_method(cls):
            """类方法必须有参数cls,这里cls传递的是类名Foo"""
            s = cls("普通属性")
            print("这里是类方法: ", s)
    
    obj = Foo()
    obj.class_method()
    
    # 输出结果:
    # 这里是类方法:  <__main__.Foo object at 0x0000016B9B8DAD68>

    (3)静态方法

    定义:使用装饰器@staticmethod. 参数随意, 没有强制要求必须是“self”或“cls”参数, 但是静态方法的方法体中不能使用类或实例的任何属性和方法.

    调用:实例对象和类对象都可以调用.

    class Foo:
    
        @staticmethod
        def static_method():
            """静态方法对参数没有固定要求,可以根据实际需要进行设置"""
            print("这里是静态方法")
    
    obj = Foo()
    obj.static_method()
    
    # 输出结果:
    # 这里是静态方法

    3.3 属性(两种形式): 字段的访问形式, 方法的表现形式

    class Person:
    
        """字段的访问形式"""
        def __init__(self, name, gender):
            self.name = name
            self.gender = gender
    
        """方法的表现形式"""
        @property
        def age(self):
            """这里是关于年龄的函数体"""
            return 18
    举例说明

    (1)字段的访问形式

    class Person:
    
        """字段的访问形式"""
        def __init__(self, name, gender):
            self.name = name
            self.gender = gender
    
    p = Person("张三", "")
    print(p.name)    # 输出结果: 张三
    print(p.gender)    # 输出结果: 男

    字段的访问形式是最常用,最普遍的

    (2)方法的表现形式

    class Person:
    
        """方法的表现形式"""
        @property
        def age(self):
            """这里是关于年龄的函数体"""
            return 18
    
    p = Person()
    print(p.age)    # 输出结果: 18

    由于对象的某些属性并不一定是固定不变的, 它可能会随着条件的改变而发生改变, 于是, 我们可以 "用方法来描述我们的属性信息".
    需要格外注意的是:

    "属性--方法的表现形式"有以下特点:

    1. 必须在方法的上方用 @property 来进行声明
    2. 该方法有且仅有一个参数 self
    3. 该方法必须有返回值

    4. 私有成员

    私有成员只能在自己的类中访问, 在类的外部无法直接访问.

    class Liu_de_hua:    # 创建一个类
        nickname = "华仔"    # 类变量
        gender = ""
        __real_age = 57    # 私有成员
    
        def __init__(self, age, wife=None):    # 构造方法
            self.age = age    # 实例变量
            self.wife = wife
            self.__wife = "朱丽倩"    # 私有成员
    
        def act(self):    # 实例方法
            print("我会表演")
    
        def date(self):    # 实例方法
            print("我常常和%s约会" % self.__wife)    # 在实例方法内部访问私有成员
    
    ldh = Liu_de_hua(48)
    
    print(ldh.wife)    # 实例对象访问实例变量
    ldh.date()    # 实例对象访问实例方法
    print(ldh.__wife)    # 尝试用实例对象访问私有成员
    
    # 执行结果:
    # None
    # 我常常和朱丽倩约会
    # 报错:  'Liu_de_hua' object has no attribute '__wife'

    私有成员的特征:

    1. 私有成员必须以 双下划线 开头, 如上面代码中的 __real_age 和 __wife .
    2. 我们可以在类的内部随意访问私有成员, 但在外部则不行.

    三. 面向对象三大特性: 封装, 继承, 多态

    关于面向对象更加准确更加规范的语言表述, 大家可以点击这里进行查看

    1. 封装: 把内容(属性或方法)封装到一个对象中, 之后调用的时候直接通过对象即可调用它们.

    (1)将内容封装到对象中

    class Person:
        def __init__(self, name, year_of_birth, gender):
            self.name = name
            self.year_of_birth = year_of_birth
            self.gender = gender
    
    p1 = Person("刘德华", "1961", "")
    p2 = Person("王菲", "1969", "")
    
    # 实例化对象p1, 将 '刘德华', '1961', '男' 分别封装到了对象p1(对象p1等同于self)内的 'name', 'year_of_birth', 'gender' 属性中
    # p2也是同样的运行过程

    (2)用对象去调用被封装的内容

    class Person:
    
        def __init__(self, name, year_of_birth, gender):
            self.name = name
            self.year_of_birth = year_of_birth
            self.gender = gender
    
    p1 = Person("刘德华", "1961", "")
    p2 = Person("王菲", "1969", "")
    
    print(p1.name)    # 输出结果:  刘德华
    print(p2.name)    # 输出结果:  王菲

    2. 继承: 子类继承父类, 子类可以继承父类中除了私有内容外的其他所有内容. Python支持多继承.

    class Foo1:
    
        __private1 = "我是父类1的私有内容1"
        class_variables1 = "我是父类1的类变量1"
    
        def instance_method1(self):
            print("我是实例方法1, 我在父类1的内部")
    
    class Foo2:
    
        __private2 = "我是父类2的私有内容2"
        class_variables2 = "我是父类2的类变量2"
    
        def instance_method2(self):
            print("我是实例方法2, 我在父类2的内部")
    
    class Bar(Foo1, Foo2):
    
        def func(self):
            print("我是子类")
    
    b = Bar()   # 实例化
    
    print(b.class_variables1)    # 子类调用类变量1
    b.instance_method1()       # 子类调用实例方法1
    
    print(b.class_variables1)    # 子类调用类变量2
    b.instance_method2()       # 子类调用实例方法2
    
    # 以上执行结果为:
    # 我是父类1的类变量1
    # 我是实例方法1, 我在父类1的内部
    # 我是父类1的类变量1
    # 我是实例方法2, 我在父类2的内部

    需要注意的是: 子类无法调用父类的私有内容, 例如在上面代码中, 如果用对象b去访问__private1则会报错.

    3. 多态: 一个对象有多种形态.

    在Python中, 多态无处不在, 因而我们无法用语言去准确描述什么是python中的多态. 或许这既是python的优点也是它的缺点吧.
    我们只能用例子来简单说明一下python中的多态, 辅助理解:

    class Animal:
        def chi(self):
            print("吃是动物的本能")
    
    class Haski(Animal):
        def chi(self):
            print("哈士奇逗比作死地吃")
    
    class Monkey(Animal):
        def chi(self):
            print("猴子张牙舞爪地吃")
    
    class si_yang_yuan:
        def wei_yang(self, animal):
            animal.chi()
    
    # 把动物全部实例化
    dong_wu = Animal()
    er_ha = Haski()
    hou_zi = Monkey()
    
    # 把饲养员实例化
    syy = si_yang_yuan()
    
    # 饲养员喂动物
    syy.wei_yang(dong_wu)
    syy.wei_yang(er_ha)
    syy.wei_yang(hou_zi)
    
    # 执行结果:
    # 吃是动物的本能
    # 哈士奇逗比作死地吃
    # 猴子张牙舞爪地吃

    从上面的例子中可以看出:
    当子类和父类存在相同的 chi() 方法时, 子类的 chi() 覆盖了父类的 chi(). 当子类调用chi()时, 会首先调用子类自己的 chi() 方法, 如果子类自己没有, 才会去父类中寻找.

    参考资料:

    1. 《面向对象三大特性介绍》-- heroliy的博客园

    2. 《python 实例方法,类方法,静态方法的区别与作用》-- 蔷薇&Nina的博客园

    3. 《面向对象 - 属性》-- 原创作者sjmicosoft

    4. 《面向对象程序设计概述 牛咏梅》-- 对立·统一 转载

  • 相关阅读:
    系统的讲解
    系统的讲解
    前端笔记之Vue(七)Vue-router&axios&Vue插件&Mock.js&cookie|session&加密
    前端笔记之Vue(六)分页排序|酷表单实战&Vue-cli
    前端笔记之Vue(五)TodoList实战&拆分store&跨域&练习代理跨域
    EggJs快速入门
    前端笔记之Vue(四)UI组件库&Vuex&虚拟服务器初识
    前端笔记之Vue(三)生命周期&CSS预处理&全局组件&自定义指令
    前端笔记之Vue(二)组件&案例&props&计算属性
    前端笔记之Vue(一)初识SPA和Vue&webpack配置和vue安装&指令
  • 原文地址:https://www.cnblogs.com/haitaoli/p/9738944.html
Copyright © 2020-2023  润新知