Bootstrap

Python之面向对象(封装,继承,多态)

 

 

目录

封装:

特点:

私有化:

Python中的dir()函数:

举例说明:

@property装饰器:

格式:

继承:

has a:

is a:

特点:

对于super关键词的用法介绍:

使用格式:

多继承(了解):

多态:


封装:

特点:

1.可理解为私有化属性

2.在类中定义公有set方法和get方法,通过调用这两个方法来操作私有化属性

3.被私有化的属性不能被继承

在Python中,封装的概念可理解为私有化。那么我们为什么要提出私有化呢,其实将属性私有化可以提升它的安全级别,访问仅限于类中,不能随意被外界修改。下面简单介绍一下私有化的概念。

私有化:

它的好处

1.隐藏属性不能随意被外界修改

2.当需要修改私有化时,可通过函数进行修改

        def setXXX(self,xxx):   #通过set对私有化属性进行重新赋值

                if xxx符合条件:

                        self.__xxx = xxx

                else:

                        不赋值

3.当需要获取私有化时,也通过函数获取

        def getXXX(self):

                return self.__xxx

Python中的dir()函数:

dir()函数不带参数时,以列表形式返回当前范围内的变量,方法和定义的类型;

带参数时,以列表形式返回参数的属性,方法。

__dir__() 的用法相同

举例说明:

class Student:
    __age = 18  # 私有化的类属性

    def __init__(self, name, age):
        self.__name = name
        self.__age = age
        self.__score = 60  # 外部无法访问,无法修改

    # 定义公有set和get方法

    # set是为了赋值
    def setAge(self, age):  # 可以改变私有化属性的值
        if age > 0 and age <= 100:
            self.__age = age
        else:
            print('年龄不符合规定值!')

    # get是为了取值
    def getAge(self):
        return self.__age

    def __str__(self):
        return '姓名:{},年龄:{},分数:{}'.format(self.__name, self.__age, self.__score)


one = Student('JSY', 20)
print(one)

one.age = 21
one.name = 99
print(one)

one.setAge(28)
print(one)

# dir(对象名/类名) 或者  (对象名/类名).__dir__()
print(dir(one))
print(dir(Student))
print(one._Student__age)  # 其实它就是__age,只是系统自动改名了,由此可见,私有化也是伪私有的
print(Student._Student__age)  # 通过该方法访问到了类的私有化属性
print(one.__dir__())

print(__name__)  # 表示当前程序运行在哪一个模块中

输出:

姓名:JSY,年龄:20,分数:60
姓名:JSY,年龄:20,分数:60
姓名:JSY,年龄:28,分数:60
['_Student__age', '_Student__name', '_Student__score', '__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', 'getAge', 'name', 'setAge']
['_Student__age', '__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__', 'getAge', 'setAge']
28
18
['_Student__name', '_Student__age', '_Student__score', 'age', 'name', '__module__', '__init__', 'setAge', 'getAge', '__str__', '__dict__', '__weakref__', '__doc__', '__repr__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
__main__

解读:第一行输出为创建对象,而分数的属性已经被固定,无需传入参数;

           第二行输出,是想要直接修改类中的私有化属性,修改失败,故属性无变化

           第三行输出,采用(set和get)函数的方式修改私有化属性,修改成功

           接下两行输出,为对象和类中的属性及方法输出

           接下两行输出,为调用系统改名后的私有化属性,故私有化本身是伪私有的

           倒数第二行输出,为使用__dir__()的方法获取对象的属性及方法

           最后一行输出,__name__方法可返回当前程序在哪一个模块中运行

@property装饰器:

开发中会使用这种方式进行私有化的处理,代替set和get,来获取和修改属性。

格式:

@property

def func1(self):                #先定义获取私有属性

        return self.__xxx      #返回私有属性值

@func1.setter                 

def func2(self,xxx):           #再定义修改私有属性

        self.__xxx=xxx

###代码示例###

class Student:
    __age = 18  # 私有化的类属性

    def __init__(self, name, age):
        self.__name = name
        self.__age = age
        self.__score = 60  # 外部无法访问,无法修改

    #先有getxxx
    @property
    def acquire_age(self):
        return self.__age
    #再有setxxx,因为get依赖set
    @acquire_age.setter
    def assignment_age(self, age):
        if age > 0 and age < 100:
            self.__age = age
        else:
            print('年龄不符合规定值 !')

    def __str__(self):
        return '姓名:{},年龄:{},分数:{}'.format(self.__name, self.__age, self.__score)


s = Student('Tom', 99)

s.assignment_age = 98  #给__age赋值
print(s.acquire_age)     #获取__age的值

输出:

98

此时,可直接通过函数名assignment_age来给私有属性__age赋一个新的值

           再通过函数acquire_age,返回私有属性__age的值

继承:

在讲继承之前我们先看一个例子:

import random

# 声明(Road)
class Road:
    def __init__(self, name, len):
        self.name = name
        self.len = len


# 声明(Car)
class Car:
    def __init__(self, brand, speed):
        self.brand = brand
        self.speed = speed

    def get_time(self, road):  # 需注意:road与r指向同一地址空间
        ran_time = random.randint(1, 10)
        message = '{}品牌的车在{}上以{}速度行驶{}小时'.format(self.brand, road.name, self.speed, ran_time)
        print(message)

    def __str__(self):
        return '{}品牌,速度:{}'.format(self.brand, self.speed)


# 创建实例化对象
r = Road('魏州大道', 6666)

c = Car('凯迪拉克', 66)
print(c)

c.get_time(r)

输出:

凯迪拉克品牌,速度:66
凯迪拉克品牌的车在魏州大道上以66速度行驶6小时

代码注意:在Car类里的get_time方法,其参数可以是一个对象类型,且该对象可以是其他类的,                    故在调用该方法的时候,可以传入Road的对象,借此来调用Road里的属性或是方法。

                  该例其实是一个 has a 的包含关系

在这个例子中我们看到了类与类直接的联系,而继承则是面向对象中的重要的类之间的关系。python中的继承可分为两种  has a 和 is a,下面介绍这两种情况。

has a:

has a 强调一种包含的关系,比如:如果A中有B,那么,B就是A的组成部分;当一个类中使用了另外一种自定义的类型(类或对象等),此时我们认为出现了has a 的包含现象。

###举例###

class Computer:
    def __init__(self, brand, type, color):
        self.brand = brand
        self.type = type
        self.color = color

    def online(self):
        print('正在玩LOL中!')

    def __str__(self):
        return self.brand + '---' + self.type + '---' + self.color


class Book:
    def __init__(self, book_name, author, number):
        self.book_name = book_name
        self.author = author
        self.number = number

    def __str__(self):
        return self.book_name + '---' + self.author + '---' + str(self.number)


class Student:
    def __init__(self, name, computer, book):
        self.name = name
        self.computer = computer
        self.books = []
        self.books.append(book)

    def borrow_book(self, input_book):
        # print('书籍信息:')
        # print(book)  #相当与调用对象名book1
        for book in self.books:
            if book.book_name == input_book.book_name:
                print('已经借过此书!')
                break
            else:
                self.books.append(input_book)
                print('借书成功!')
                break
        print(self.books)

    def show_book(self):
        for book in self.books:
            print(book.book_name)

    def __str__(self):
        return self.name + '---' + str(self.computer) + '---' + str(self.books)  # 不能返回一个对象


# 创建对象
computer = Computer('hp', 'pro++', 'red')  # computer 对象类型
book = Book('武动乾坤', '天蚕土豆', 15)

stu = Student('JJ', computer, book)  # 此时出现了  包含关系  (stu对象包含了computer对象和book对象)
print(stu)

book1 = Book('斗破苍穹', '天蚕土豆', 20)

stu.show_book()
stu.borrow_book(book1)

输出:

JJ---hp---pro++---red---[<__main__.Book object at 0x0000021AA3F7C610>]
武动乾坤
借书成功!
[<__main__.Book object at 0x0000021AA3F7C610>, <__main__.Book object at 0x0000021AA3F7C520>]
武动乾坤
斗破苍穹

从代码:stu = Student('JJ', computer, book) 可以看出,定义一个Student对象的时候,传入了                Computer的对象和Book的对象,故在Student类中可以对它们的(非私有化)属性进行调                用。

is a:

is a 可以说是真正意义上的继承关系:如果A是B的一种,那么B就是A的基类(base class / 父类);比如:等边三角形是三角形,则三角形是等边三角形的基类。

特点:

1.私有化属性不能被继承

2.如果子类中没有定义__init__,自动调用父类的__init__方法

3.如果子类继承父类需要定义自己的__init__方法(来初始化新的属性等操作),需要在__init__里先     调用一下父类的__init__,再去初始化新的属性或其他操作。

4.调用父类的方法:super关键词

5.如果父类有eat(),子类也定义一个eat()方法,按照就近原则:先找当前类,再去找父类

   如果子类出现父类同名的方法,可称之为 override--重写(覆盖)。

   此情况一般出现在:父类提供的方法不能满足子类的需求

6.子类中可调用父类的同名方法:

   使用super().方法名(参数)    #可调用父类的方法,再写新增的方法(内容)

对于super关键词的用法介绍:

Python中的super()方法设计目的是用来解决多重继承时父类的查找问题,所以在单重继承中用不用super都没关系;但是,使用super()是一个好的习惯。一般我们在子类中需要调用父类的方法时才会这么用。

1.super(cls,obj)      #即传入类名+对象名

obj对象必须是cls类的对象(cls的子类的对象当然也是cls类的对象),记作 type(obj) <= cls;具有判断功能

2.super(cls1,cls2)   #即传入两个类名

cls2必须是cls1的子类或是本身,记作cls2<=cls1 (右的范围小于左)具有判断功能

3.super().__init()      #等同于  super(A,self).__init__()

使用格式:

class Student(Person):     #可理解为所有学生都是人,Person是父类

        pass

###举例说明###

# 可以定义一个“总类”
class Person:
    def __init__(self, name,age):
        self.name = name
        self.age = age

    def eat(self):
        print(self.name + '正在吃饭!','年龄:'+str(self.age))


# 在定义这种相似的类时,可能会出现许多重复的属性或方法,此时需要使用到*继承*
class Student(Person):  # 此为继承的格式
    def __init__(self, name,age):
        print('--->Student的init')
        # 如果要定义自己的__init__,还需调用父类的__init__;可使用super来调用父类的方法
        # super(Student, self).__init__()  # super另一种使用方式
        super().__init__(name,age)  # super() == 父类Person ;调用父类的__init__方法
        super(Student, self).__init__()



class Employee(Person):
    def __init__(self,name,age,work):  #新增的属性work,需要在本类中进行处理
        print('--->Employee的init')
        super().__init__(name,age) #调用父类的__init__先将self.name 和 self.age 获取
        self.work = work     #独有属性在本类的__init__里进行初始化
    def __str__(self):
        return '姓名:'+self.name+'年龄:'+str(self.age)+'职业:'+self.work
    
    def eat(self):
        super(Employee, self).eat()  #在子类中调用父类的同名方法,再向下写新增的内容
        print(self.name+'正在吃饭! ')


class Doctor(Person):
    def __init__(self,name,age,department):
        super(Doctor, self).__init__(name,age)  #super(类名,对象).__init__();底层进行判断:判断该对象是否是以这个类构建出来的
        self.department = department
    def __str__(self):
        return '姓名:'+self.name+'年龄:'+str(self.age)+'部门:'+self.department

s = Student('Jack',16)
s.eat()
print(s.name)

e = Employee('Tom',29,'程序员')
print(e)
e.eat()    #此时由于Employee本类中就有eat方法,无需去调用父类(Person)里的eat方法,故此为就近原则

d = Doctor('华佗',88,'老中医')
print(d)
d.eat()

输出:

--->Student的init
Jack正在吃饭! 年龄:16
Jack
--->Employee的init
姓名:Tom年龄:29职业:程序员
Tom正在吃饭! 年龄:29
Tom正在吃饭! 
姓名:华佗年龄:88部门:老中医
华佗正在吃饭! 年龄:88

本例中,Person为父类,Student,Employee,Doctor,均继承它,而这些子类都有一些本身才具有的性质,故需要重写父类的__init__方法;此外,继承中的检索按照就近原则,先检索本类,再到父类。

多继承(了解):

Python允许多重继承:一个子类可以有多个父类(java不可以)       #开发中很少使用

        class  子类(父类1,父类2...):

                pass

多重继承的搜索顺序:在Python2中,对于经典类和新式类有所不同

经典类:  #Python2中是从左至右,深度优先

class Base:

        pass

class A(Base):

        pass

class B(Base):
        pass

新式类:   

class Base(object):   #区别在此,python2中是广度优先

        pass

class A(Base):

        pass

class B(Base):

        pass

注意:在python3里,无论经典类还是新式类都是广度优先

           查看搜索顺序的方法:1.类名.__mro__     #直接使用查看

                                               2.import inspect   #导包后,使用getmro()方法查看

                                                  print(inspect.getmro(类名))

###举例说明###

#C继承了A和B,而A和B继承了Base
#Base-->A,B-->C
class Base:
    def test(self):
        print('--->Base')


class A(Base):
    def test1(self):
        print('--->A')

class B(Base):
    def test2(self):
        print('--->B')

class C(A,B):   #同时继承多个父类
    def test(self):
        print('--->C')

import inspect
print(inspect.getmro(C))  #显示的可理解为搜索顺序
print(C.__mro__)          #同上

c = C()
c.test1()  #可以使用多个父类提供的方法
c.test2()
# c.test3()
c.test()  #出现与父类同名方法时,搜索顺序为就近原则

输出:

(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)
--->A
--->B
--->C

注意:前两行输出为继承类的搜索顺序

          最后的输出是 调用的C类中的test方法,由于出现与父类同名的方法,搜索顺序为就近原则

多态:

实际上,python并无严格多态概念。多态可理解为:一种事物的多个形态,对于继承可以认为是为了精简代码,而多态是对精简后代码的灵活使用,因而多态依赖于继承。

##示例##

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

    def feed_pet(self,pet):  #此时出现了多态;参数pet既可以接收cat,也可以接收dog,还可以接收tiger(不是Pet的子类)或其他对象
        if isinstance(pet,Pet): #判断了pet是否是Pet或Pet子类的对象
            print('{}喜欢养宠物:{},昵称是:{}'.format(self.name,pet.role,pet.nickname))
        else:
            print('这个不是宠物!可能有危险!!!')
class Pet:
    role = 'Pet'
    def __init__(self,nickanme,age):
        self.nickname = nickanme
        self.age = age

    def show(self):
        print('昵称:{},年龄:{}'.format(self.nickname,self.age))

class Cat(Pet):
    role = '猫'
    def catch_mouse(self):
        print('抓老鼠...')

class Dog(Pet):
    role = '狗'
    def watch_house(self):
        print('看家...')

class Tiger:
    def eat(self):
        print('凶猛东北虎!')

cat = Cat('妙妙',3)
dog = Dog('旺财',2)
tiger = Tiger()
p = Pet('山竹',1)
person = Person('JJ')

person.feed_pet(cat)  #此时涉及到了多态
person.feed_pet(tiger)
person.feed_pet(p)

输出:

JJ喜欢养宠物:猫,昵称是:妙妙
这个不是宠物!可能有危险!!!
JJ喜欢养宠物:Pet,昵称是:山竹

注意:

1.对同一个函数feed_pet(),传入不同参数(对象),可以实现不同功能。

2.由于python中所有类均继承于祖先类Object,故多态总能应用到很多场景(无论类与类之间是否有继承关系)。

;