Python面向对象-特性

Posted onetoinf

tags:

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

封装

我们可以给一个实例绑定很多属性,如果有些属性不希望被外部访问到怎么办?

Python对属性权限的控制是通过属性名来实现的,如果一个属性由双下划线开头(__),该属性就无法被外部访问。看例子:

class Person(object):
    def __init__(self, name):
        self.name = name
        self._title = 'Mr'
        self.__job = 'Student'
p = Person('Bob')
print p.name
# => Bob
print p._title
# => Mr
print p.__job
# => Error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Person' object has no attribute '__job'

可见,只有以双下划线开头的"__job"不能直接被外部访问。

但是,如果一个属性以"__xxx__"的形式定义,那它又可以被外部访问了,以"__xxx__"定义的属性在Python的类中被称为特殊属性,有很多预定义的特殊属性可以使用,通常我们不要把普通属性用"__xxx__"定义。

以单下划线开头的属性"_xxx"虽然也可以被外部访问,但是,按照习惯,他们不应该被外部访问。

python的类没有访问权限的问题,也就是说所有的变量都是可访问的。实际上python有私有的机制,就是在属性前加__,但是这种私有机制实际上也是伪私有,因为它其实是用一个别名来保存这个属性。例如在类A中的self.__a = 4, 实际上__a被修改成了_A__a保持在类中了。

示例

请给Person类的__init__方法中添加namescore参数,并把score绑定到__score属性上,看看外部是否能访问到。

以双下划线开头的属性无法被外部访问,"__xxx__"除外。

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

p = Person('Bob', 59)

print p.name
print p.__score

继承

如果已经定义了Person类,需要定义新的StudentTeacher类时,可以直接从Person类继承:

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

定义Student类时,只需要把额外的属性加上,例如score

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score

一定要用 super(Student, self).__init__(name, gender)去初始化父类,否则,继承自 PersonStudent 将没有 namegender

函数super(Student, self)将返回当前类继承的父类,即 Person ,然后调用__init__()方法,注意self参数已在super()中传入,在__init__()中将隐式传递,不需要写出(也不能写)。

继承的子类:
1、会继承父类的属性和方法;
2、也可以自己定义,覆盖父类的属性和方法
super()调用父类的方法:

class A(object):
    def method(self.arg):
        pass

class B(A):
    def method(self, arg):
        super(B, self).method()

用类名调用父类的方法:

class A(object):
    def method(self.arg):
        pass

class B(A):
    def method(self, arg):
        A.method(arg)

类型判断

  • isinstance # 判断类型
  • issubclass # 判断是否是子类
class Programer(object):
    hobby = 'Play Computer'
    def __init__(self, name,age,weight):
        self.name = name
        self._age = age
        self.__weight = weight

    @classmethod
    def get_hobby(cls):
        return cls.hobby

    @property
    def get_weight(self):
        return self.__weight

    def self_introduction(self):
        print(('My Name is %s\nI am %s years old\n') % (self.name,self._age))

class BackendProgramer(Programer):
    def __init__(self,name,age,weight,language):
        super(BackendProgramer,self).__init__(name,age,weight)
        self.language = language      # 定义language这个属性

if __name__ == '__main__':
    programer = BackendProgramer('Albert',25,80,'Python')
    print(dir(programer))
    print(programer.__dict__)
    print(type(programer))
    print(isinstance(programer,Programer))  # 判断是否是Programer这个类

运行结果:

['_Programer__weight', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_age', 'get_hobby', 'get_weight', 'hobby', 'language', 'name', 'self_introduction']
{'_Programer__weight': 80, '_age': 25, 'language': 'Python', 'name': 'Albert'}
<class '__main__.BackendProgramer'>
True

多重继承

除了从一个父类继承外,Python允许从多个父类继承,称为多重继承。

多重继承的继承链就不是一棵树了,它像这样:

class A(object):
    def __init__(self, a):
        print 'init A...'
        self.a = a

class B(A):
    def __init__(self, a):
        super(B, self).__init__(a)
        print 'init B...'

class C(A):
    def __init__(self, a):
        super(C, self).__init__(a)
        print 'init C...'

class D(B, C):
    def __init__(self, a):
        super(D, self).__init__(a)
        print 'init D...'

像这样,D 同时继承自 B 和 C,也就是 D 拥有了 A、B、C 的全部功能。多重继承通过 super()调用__init__()方法时,A 虽然被继承了两次,但__init__()只调用一次:

>>> d = D('d')
init A...
init C...
init B...
init D...

多重继承的目的是从两种继承树中分别选择并继承出子类,以便组合功能使用。

举个例子,Python的网络服务器有TCPServer、UDPServer、UnixStreamServer、UnixDatagramServer,而服务器运行模式有 多进程ForkingMixin 和 多线程ThreadingMixin两种。

要创建多进程模式的 TCPServer:

class MyTCPServer(TCPServer, ForkingMixin)
    pass

要创建多线程模式的 UDPServer:

class MyUDPServer(UDPServer, ThreadingMixin):
    pass

如果没有多重继承,要实现上述所有可能的组合需要 4x2=8 个子类。

示例

+-Person
+- Student
+- Teacher

是一类继承树;

+- SkillMixin
+- BasketballMixin
+- FootballMixin

是一类继承树。

通过多重继承,请定义“会打篮球的学生”和“会踢足球的老师”。

多重继承需要从两个或更多的类派生。

参考代码:

class Person(object):
    pass

class Student(Person):
    pass

class Teacher(Person):
    pass

class SkillMixin(object):
    pass

class BasketballMixin(SkillMixin):
    def skill(self):
        return 'basketball'

class FootballMixin(SkillMixin):
    def skill(self):
        return 'football'

class BStudent(Student, BasketballMixin):
    pass

class FTeacher(Teacher, FootballMixin):
    pass

s = BStudent()
print s.skill()

t = FTeacher()
print t.skill()

多态

类具有继承关系,并且子类类型可以向上转型看做父类类型,如果我们从 Person 派生出 Student和Teacher ,并都写了一个 whoAmI() 方法:

class Person(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def whoAmI(self):
        return 'I am a Person, my name is %s' % self.name

class Student(Person):
    def __init__(self, name, gender, score):
        super(Student, self).__init__(name, gender)
        self.score = score
    def whoAmI(self):
        return 'I am a Student, my name is %s' % self.name

class Teacher(Person):
    def __init__(self, name, gender, course):
        super(Teacher, self).__init__(name, gender)
        self.course = course
    def whoAmI(self):
        return 'I am a Teacher, my name is %s' % self.name

在一个函数中,如果我们接收一个变量 x,则无论该 x 是 Person、Student还是 Teacher,都可以正确打印出结果:

def who_am_i(x):
    print x.whoAmI()

p = Person('Tim', 'Male')
s = Student('Bob', 'Male', 88)
t = Teacher('Alice', 'Female', 'English')

who_am_i(p)
who_am_i(s)
who_am_i(t)

运行结果:

I am a Person, my name is Tim
I am a Student, my name is Bob
I am a Teacher, my name is Alice

这种行为称为多态。也就是说,方法调用将作用在 x 的实际类型上。s 是Student类型,它实际上拥有自己的 whoAmI()方法以及从 Person继承的 whoAmI方法,但调用 s.whoAmI()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止。

由于Python是动态语言,所以,传递给函数 who_am_i(x)的参数 x 不一定是 Person 或 Person 的子类型。任何数据类型的实例都可以,只要它有一个whoAmI()的方法即可:

class Book(object):
    def whoAmI(self):
        return 'I am a book'

这是动态语言和静态语言(例如Java)最大的差别之一。动态语言调用实例方法,不检查类型,只要方法存在,参数正确,就可以调用。

多态的要素:继承和方法重写

class Programer(object):
    hobby = 'Play Computer'
    def __init__(self, name,age,weight):
        self.name = name
        self._age = age
        self.__weight = weight

    @classmethod
    def get_hobby(cls):
        return cls.hobby

    @property
    def get_weight(self):
        return self.__weight

    def self_introduction(self):
        print(('My Name is %s\nI am %s years old\n') % (self.name,self._age))

class BackendProgramer(Programer):
    def __init__(self,name,age,weight,language):
        super(BackendProgramer,self).__init__(name,age,weight)
        self.language = language
    # 重写父类的 self_introduction方法
    def self_introduction(self):
        print(('My Name is %s\nMy favorite language is %s\n') % (self.name,self.language))
# 判断传进来的参数是否属于Programer对象,如果是,直接调用对象的self_introduction()方法
def introduce(programer):
    if isinstance(programer,Programer):
        programer.self_introduction()

if __name__ == '__main__':
    # 将对象实例化
    programer = Programer('Albert',25,80)
    backend_programer = BackendProgramer('Tim',30,70,'Python')
    introduce(programer)
    introduce(backend_programer)

运行结果:

My Name is Albert 
I am 25 years old 

My Name is Tim 
My favorite language is Python

多态优点:
添加功能比较简单,只要判断是否属于这个父类,而不需要判断传进来的是哪个子类,直击调用这个方法。

示例

Python提供了open()函数来打开一个磁盘文件,并返回 File 对象。File对象有一个read()方法可以读取文件内容:

例如,从文件读取内容并解析为JSON结果:

import json
f = open('/path/to/file.json', 'r')
print json.load(f)

由于Python的动态特性,json.load()并不一定要从一个File对象读取内容。任何对象,只要有read()方法,就称为File-like Object,都可以传给json.load()。

请尝试编写一个File-like Object,把一个字符串 r‘["Tim", "Bob", "Alice"]‘包装成 File-like Object 并由 json.load() 解析。

只要为Students类加上 read()方法,就变成了一个File-like Object。

import json

class Students(object):
    def read(self):
        return r'["Tim", "Bob", "Alice"]'

s = Students()

print json.load(s)

以上是关于Python面向对象-特性的主要内容,如果未能解决你的问题,请参考以下文章

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

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

面向对象三大特性

继承 多态 封装 Python面向对象的三大特性

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

Python之面向对象继承详解以及面向对象三大特性