Python基础-第六天-面向对象编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python基础-第六天-面向对象编程相关的知识,希望对你有一定的参考价值。

本篇内容

1.面向对象编程介绍

2.类的定义及各部分的介绍

3.属性

4.方法

5.面向对象的特性-封装

6.面向对象的特性-继承

7.面向对象的特性-多态

8.新式类和经典类



一、面向对象编程介绍

1.编程的原则 

无论用什么编程范式来编程都要记住的原则是,避免写重复代码,代码要易扩展。一定要遵循可读性好、易扩展的原则。


2.面向对象编程(Object-Oriented Programming)介绍

OOP编程的主要作用是使代码修改和扩展变的更容易;

面向对象编程是利用类和对象来帮助我们实现功能的。类里面放的是函数,每一个函数可以为一个功能,不同的是,我们不能在类的外面直接调用函数,必须借助对象来调用函数,而对象是通过实例化类生成的。

在Python中一切皆对象,Python中字符串、列表等等都是一个对象。

每一个对象都有属性,例如对象的名字、颜色等等都是它的属性。

每一个对象都有功能(即方法),比如append()就是列表其中一个功能。

在Python中通过“.”来调用对象的功能。


3.面向对象的概念和特性

①概念

●Class(类)

一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法(也可以理解为功能);

 

●Object(对象)

一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之间有共性,亦有不同。

通过类生成了对象就叫实例化,对象也可以称为实例;


②特性

●Encapsulation(封装)
在类中对数据的赋值、内部调用对外部用户是透明的、不可见的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法;

作用:1.防止数据被随意修改;2.使外部程序不需要关注对象内部的构造或逻辑,只需要通过对象对外提供的接口进行访问即可;3.避免写重复代码;

 

●Inheritance(继承)
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承。

通过子类继承父类的方式,可以用最少代码量实现不同的对象既具有共同点,又具有不同点。

继承主要的作用就是避免写重复代码;

 

●Polymorphism(多态)
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。



二、类的定义及各部分的介绍

1.类的定义

class Person(object):  # 类名中的所有单词首字母要大写,采用驼峰命名法(例CamelCase)的书写方式;

    # 在类中直接定义的属性即是公有属性
    nationality = "CN"

    def __init__(self, name, age):  # 构造函数
        self.Name = name  # 普通属性,相当于p1.Name = "Jack"
        self.age = age

        # 私有属性外部是不可见的,内部是可见的
        self.__money = "1W"

    def talk(self):  # 类的方法函数
        print("%s 说的是中文" % self.Name)

    def get_money(self):  # 类的方法函数,这个函数的作用是对外部提供私有属性只读访问接口

        # 私有属性只能在类的内部调用,不能在类的外部调用
        self.__money = "2W"
        print(self.__money)

        return self.__money
        
    def __del__(self):  # 析构函数
        print("实例: %s消亡了" % self.Name)

①构造函数

__init__这个函数就是构造函数,构造函数中定义了有普通属性

在实例化时传递的参数都传递到了构造函数中。如果不需要给构造函数传递参数,则不用定义构造函数;


②类的方法函数

定义所有对象都具有的共同功能

类的方法函数是公有的方法,只在类中保存了一份


③公有属性(也称为静态属性)

在类中直接定义的属性即是公有属性,公有属性和构造函数、类的方法函数是平级的。下面会详细介绍


④私有属性

私有属性只能在内部被调用,不能在外部被调用。


⑤析构函数

析构函数在实例被销毁后就会自动执行。上面的__del__就是一个析构函数;

使用del删除对象和程序结束时会销毁实例,这时析构函数也会执行;

析构函数可以用来做程序的收尾工作,比如关闭打开的文件等;


⑥self的作用

其实self就是实例本身,实例化时python会自动把这个实例本身通过self参数传进去。self的存在就是为了解决类中在所有的函数中都能访问对象属性的问题。


2.实例化

p1 = Person("Jack", 33)  # 实例化类生成对象,相当于Person(p1, "Jack", 26)
p2 = Person("Eric", 33)  # 实例化类生成对象,相当于Person(p2, "Eric", 28)

注意,虽然p1和p2都是通过Person类实例化出来的,但p1和p2是两个不通的对象。比如,人类代表所有人,但人是具体到每个不同的人,这里Person类是人类,p1和p2是一个个具体的人。


3.调用对象方法

p1.talk()  # 调用人的说话功能,相当于d1.talk(p1)

当类的方法函数需要另外传参数时,在调用该方法函数时要在括号中写上参数;



三、属性

属性分为:成员属性、私有属性和公有属性(静态属性);


1.成员属性

在实例化时,传递给构造函数初始化的属性称为成员属性,又可以称为成员变量或实例属性;

上面的Name和age都是成员属性;


2.私有属性

私有属性只能在内部被调用,不能在外部被调用。

私有属性是在构造函数中定义的;

私有属性的命名规则是两个下划线加变量名,上面的__money是私有属性;

由于私有属性只能在内部被调用,当在外部需要调用时怎么办?上面的get_money方法函数就是提供外部调用私有属性的接口,通过return返回私有属性。


3.公有属性(静态属性)

所有属于这个类的对象都可以访问的属性即是公有属性

在类中直接定义的属性即是公有属性,公有属性和构造函数、类的方法函数是平级的。上面的nationality就是一个公有属性;

为什么需要公有属性,试想一下,现在有一个需求,需要通过中国类生成所有中国人,生成的每个中国人都是一个独立的对象,他们有一个共同的国籍属性都为中国,而实例属性是封装在每个对象中,那问题就来了,国籍属性需要存十三亿份,这时使用公有属性,就只会在类中存一份。


①访问公有属性的方法

●通过类直接访问

print(Person.nationality)  # 直接去类中查找,找到了就返回,没找到就报错。


●通过对象访问

# 先去对象中查找,找到了就直接返回,如果没有找到,再去类中查找,找到了就返回,没找到就报错。
print(p1.nationality)


②修改公有属性的值

●通过类修改

# 通过类修改公有属性,修改的是保存在类中的公有属性的值。是全局修改,修改的是所有
Person.nationality = "JP"


●通过对象修改

# 通过对象修改公有属性,会在对象中直接初始化一个成员属性,名字为公有属性的名字,值为要修改成的内容。
p1.nationality = "JP"



四、方法

类的方法分为:普通方法(或动态属性)、静态方法


1.普通方法(或动态属性

普通方法是公有的方法,如上面的talk、get_money函数就是普通方法,只在类中保存了一份;

普通方法的调用者是对象,要通过对象来调用普通方法;

普通方法至少需要有一个self参数;


2.静态方法

为什么需要静态方法?假如我们创建了一个类,类中没有构造函数,只有方法,在这样的情况下,如果要调用类中的方法,就必须先要实例化出对象,然后通过对象来调用类中的方法。那么这样干,就导致了在类和对象都在占用了内存空间的情况下,和单独用函数来做实现的效果一样,并且函数占用的内存空间还小一些,这时我们就需要用静态方法来解决这个问题。

静态方法允许在不创建对象的情况下直接被调用,就是不需要通过对象来调用,可以直接通过类来调用;上面的

在方法函数上面加上@staticmethod后,方法函数就变成了静态方法:

@staticmethod
def talk(self):  # 静态方法
        print("%s 说的是中文" % self.Name)
        
        
Person.talk()  # 通过类直接调用



五、面向对象的特性-封装

封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。

封装在类中的有:公有属性、构造函数、动态属性(或普通方法)、静态方法;

封装在对象中的有:成员属性(普通属性)的值;

在使用面向对象的封装特性时,需要知道:将内容封装到某处,就要从某处调用被封装的内容。

调用被封装的内容时,有两种情况:通过对象直接调用、通过self间接调用(在类的方法函数中,需要通过self间接调用被封装的内容);



六、面向对象的特性-继承

1.什么是继承

面向对象中的继承和现实生活中的继承相同,即子可以继承父的内容。

继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。所以继承的主要功能就是代码重用;

通过继承创建的新类称为子类或派生类。被继承的类称为基类、父类或超类;


2.实现继承

要实现继承,可以通过继承(Inheritance)和组合(Composition)两种方式来实现。

在某些OOP语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现继承多个类,可以通过多重继承(同时继承多个类)和多级继承(父类下面有子类,子类下面还有孙子类)两种方式来实现。

继承概念的实现方式主要有两种:实现继承、接口继承。实现继承是指使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构父类方法);


3.继承的语法

●单继承

class Person(object):  # 父类

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

    def talk(self):
        print("人在说话")


class BlackPerson(Person):  # 括号中写所继承的类

    def walk(self):
        print("黑人在走路")
        
# BlackPerson类继承了Person类的构造函数和talk方法

所以,对于面向对象的继承来说,其实就是将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法。


●多继承

class SchoolMenber(object):
    """学校成员基类"""
    member = 0  # 学校成员的总人数

    def __init__(self, name, age, sex):
        """
        :param name: 姓名
        :param age: 年龄
        :param sex: 性别
        """""
        self.name = name
        self.age = age
        self.sex = sex
        self.enroll()  # 只要一实例化就会自动调用注册功能自动注册

    def enroll(self):
        """注册功能"""
        print("注册一个新的学校成员: %s" % self.name)
        SchoolMenber.member += 1  # 每注册一个新成员,公有属性member就会加一

    def tell(self):
        """ 打印成员信息"""
        print("----- info: %s -----" % self.name)
        for key, value in self.__dict__.items():
            print("\t%s: %s" % (key, value))

    def __del__(self):
        """析构函数,在实例销毁时自动执行"""
        print("成员: %s被开除了" % self.name)
        SchoolMenber.member -= 1


class School(object):
    """学校类"""
    def open_branch(self, addr):
        """
        开分校的功能
        :param addr: 分校的地址
        :return:
        """""
        print("新的分校位于: %s" % addr)


class Teacher(SchoolMenber, School):  # 括号中写所继承的类
    """讲师类"""
    def teaching(self):
        """教学功能"""
        print("%s讲师正在讲%s课程" % (self.name, self.course))

当通过Teacher类实例化出的对象调用方法时,先在Teacher类中查找,没有找到再去SchoolMenber类中查找,最后去School类中找,如果还没找到就报错。


4.继承和重构

有时候我们需要继承父类中的方法,并且还要在该方法中加点其它东西怎么办?

class Person(object):  # 父类

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

    def talk(self):
        print("人在说话")


class BlackPerson(Person):  # 括号中写所继承的类

    # 先继承,再重构
    # self把当前这个实例也传递进来了,所以self代表当前这个实例
    # 实例化时name、age是传递给父类的,strength是传递给子类的
    def __init__(self, name, age, strength):

        # 继承Person类中的构造函数
        # self是在实例化时将当前实例传递进来给子类,子类再将当前这个实例传递给父类
        # name和age是在实例化时传递进来给子类的两个参数,子类再将这两个参数传递给父类
        # 经典类写法和新式类写法效果一样,现在主要写新式类
        # Person.__init__(self, name, age)  # 经典类写法
        super(BlackPerson, self).__init__(name, age)  # 新式类写法

        self.strength = strength  # 子类独有的属性

    def talk(self):

        # 继承Person类中的talk方法函数
        # self是在实例化时将当前实例传递进来给子类,子类再将当前这个实例传递给父类
        # Person.talk(self)  # 经典类写法
        super(BlackPerson, self).talk()  # 新式类写法

        print("黑人在说话")

    def walk(self):
        print("黑人在走路")



七、面向对象的特性-多态

多态是面向对象的重要特性,简单点说:一个接口,多种实现;

接口重用,但表现形式却不一样;

python中不直接支持多态,但可以间接实现,崇尚“鸭子类型”。

# Python “鸭子类型”
class F1:
    pass
 
 
class S1(F1):
 
    def show(self):
        print("S1.show")
 
 
class S2(F1):
 
    def show(self):
        print("S2.show")
 
 
def Func(obj):
    print obj.show()
 
 
s1_obj = S1()
Func(s1_obj)
 
s2_obj = S2()
Func(s2_obj)



八、新式类和经典类

1.什么是新式类和经典类

经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

技术分享

技术分享


2.新式类和经典类的继承顺序

class D:
    def bar(self):
        print(‘D.bar‘)


class C(D):
    def bar(self):
        print(‘C.bar‘)


class B(D):
    def bar(self):
        print(‘B.bar‘)


class A(B, C):
    def bar(self):
        print(‘A.bar‘)


a = A()
a.bar()

●Python 2.X的:

经典类(深度优先):首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错

新式类(广度优先):首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错


●Python 3.X的:

不管是经典类还是新式类,采用的都是广度优先;


注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了。

技术分享


本文出自 “12031302” 博客,谢绝转载!

以上是关于Python基础-第六天-面向对象编程的主要内容,如果未能解决你的问题,请参考以下文章

python运维开发之第六天

python第六天:一些常用函数,面向对象入门

我的Python成长之路---第六天---Python基础(20)---2016年2月20日(晴)

Python全栈开发学习笔记-06.第六天

python之路第六天

C#基础第六天-作业答案-利用面向对象的思想去实现名片