python基础教程---更加抽象,类,继承多态封装

1.对象

对象基本可以看做数据(特性)以及由一系列可以存取,操作这些数据的方法所组成的集合

多态,不同类的对象使用同样的操作

封装,对外部世界隐藏对象的工作细节

继承,以普通的类为基础建立专门的类对象

1.1多态

意味着,就算不知道变量所引用的对象类型是什么,还是能对它进行操作,而它也会根据对象(或类)类型的不同而表现出不同的行为

绑定到对象特性上面的函数成为方法,如count, '+'

标准库random中包含choice函数可以从序列中随机选出元素,给变量赋值

>>> from random import choice

>>> x = choice(['Hello, world!',  [1,2.'e','e',4] }

>>> x.count('e')         2

说明列表胜出,关键点在于不需要检测类型,只需要知道x有个叫做count的方法,带有一个字符作为参数,并返回整数值

绑定到对象特性上面的函数称为方法

多态的多种形式

任何不知道对象到底是什么类型,但是又要对对象‘做点什么’的时候都会用到多态

不仅限于方法,很多内建运算符和函数都有多态的性质,如加运算符

1.2封装

对全局作用域中其他区域隐藏多余信息的原则

多态是可以让用户对于不知道是什么类(或对象类型)的对象进行方法调用,而封装是可以不用关心对象是如何构建的而直接进行使用

1.3继承

2.类和类型

2.1类到底是什么

所有对象都属于某一个类,称为类的实例

当一个对象所属的类是另外一个对象所属类的子集时,前者被称为后者的子类,相反为超类

2.2创建自己的类

__metaclass__ = type   #确定使用新式类

class Person:

    def  setName(self, name):

          self.name = name

    def  getName(self):

          return self.name

    def greet(self):

         print "Hello, world! I'm %s." %self.name

Person是类的名字,class语句会在函数定义的地方创建自己的命名空间,self是对于对象自身的引用

>>> foo = Person()

>>> bar = Person()

>>> foo.setName('Luke Skywalker')

>>> bar.setName('Anakin Skywaylker')

>>> foo.greet()             Hello. world! I'm Luke Skywalker.

>>> bar.greet()             Hello. world! I'm Anakin Skywalker.

特性是可以在外部访问的

>>> foo.name      'Luke Skywalker'

>>> bar.name = 'Yoda'

>>> bar.greet()          Hello.world! I'm Yoda.

2.3特性,函数和方法

方法(绑定方法)将他们第一个参数绑定到所属的实例上,因此这个参数就可以不必提供,所以可以将特性绑定到一个普通函数上,这样就不会有特殊的self参数了

>>> class Class:

              def   method(self):

                      print "I have a self!"

>>> def function():

              print " I don't ..."

>>> instance = Class()

>>> instance.method()                      I  have  a  self!

>>> instance.method = function

>>> instance.method()                      I  don't ...

self参数并不取决于调用方法的方式,目前使用的是实例调用方法,可以随意使用引用同一个方法的其他变量

>>> class Bird:

              song = 'Squaawk!"

              def sing(self):

                    print self.song

>>> bird = Bird()

>>> bird.sing()              Squaawk

>>> birdsong = bird.sing

>>> birdsong()             Squaawk

尽管最后一个方法调用看起来与函数调用类似,但变量birdsong引用绑定方法bird.sing上,也就意味着还是对self参数的访问(即依旧绑定到类的相同实例上)

私有化

默认情况下,程序可以从外部访问一个对象的特性,再次使用前面讨论过的有关封装的例子:

>>> c.name       'Sir Lancelot'

>>> c.name = 'Sir Gumby'

>>> c.getName()     'Sir Gumby'

有些程序员认为这样做是可以的,但有些(如SmallTalk之父,SmallTalk的对象特性只允许由同一个对象的方法访问)觉得这样破坏了封装的原则

他们认为对象的状态对于外部应该是完全隐藏(不可访问)的,因为

其他程序员可能不知道(可能也不应该知道)你的对象内部的具体操作,例如ClosedObject可能会在其他对象更改自己名字的时候,给一些

管理员发送邮件消息,这应该是setName方法的一部分,但是如果直接用c.name设定名字会发生什么?什么都没发生,Email也没有发出去,为了避免这类事件,应该使用私有特性,这是外部对象无法访问,但getName和setName等访问器能够访问的特性

为了让方法或者特性变为私有(从外部无法访问),只要在它的名字前面加上双下划线即可:

class Secretive:

        def   __inaccessible(self):

                 print  "Bet you can't see me..."

        def accessible(self):

                 print  "The secret message is:"            

                 self.__iaccessible()

现在__inaccessible从外界是无法访问的,而在类内部还能使用(比如从accessible)访问:

>>> s = Secretive()

>>> s.__inaccessible()               报错AttributeError: Secretive instance has no attribute '__inaccessible'

>>> s.accessible()

The secret message is:

Bet you can't see me...

尽管上下划线有些奇怪,但是看起来像是其他语言中的标准的私有方法,真正发生的事情才是不标准的。

类的内部定义中,所有以上下划线开始的名字都被“翻译:成前面加上单下划线和类名的形式

>>> Secretive._Secretive__inaccessible


在了解这些幕后之后,实际上还是能在类外访问这些私有方法,尽管不应该这么做:

>>> s._Secretice__inaccessible()

Bet you can't see me..

简而言之,确保其他人不会访问对象的方法和特性是不可能的,但是这类”名称变化术“就是他们不应该访问这些函数或特性的信号

如果不需要使用这种方法但是又想让其他对象不要访问内部数据,那么可以使用单下划线,

这不过是个习惯,但有实际效果,例如前面有下划线的名字都不会被带星号的imports语句(from module import*)导入

2.4类的命名空间

下面两个语句几乎等价

def  foo(x):  return x*x

foo = lambda x: x*x

两者都创建了返回参数平方的函数,而且都将变量foo绑定到函数上。

变量foo可以在全局(模块)范围进行定义,也可处于局部的函数或方法内,

定义类时,同样的事情也会发生,所有位于class语句中的代码都在特殊的命名空间中执行----类命名空间。

这个命名空间可由类内所有成员访问,并不是所有python程序员都知道类的定义其实就是执行代码块,比如,在类的定义区并不只限使用def语句:

>>> class C:

             print 'Class C being defined...'

Class C being defined...

>>>

下面例子,

class MemberCounter:

      menbers = 0

      def init(self):

          MemberCounter.members += 1

>>> m1 = MemberCounter()

>>> m1.init()

>>> MemberCounter.members                 1

>>> m2 = MemberCounter()

>>> m2.init()

>>> MemberCounter.memberd                 2

上面代码中,在类作用域内定义了一个可供所有成员)实例)访问的变量,用来计算类的成员数量。注意init用来初始化所有实例

类作用域内的变量也可以被所有实例访问:

>>> m1.members                2

>>> m2.members                2

重绑定members特性

>>> m1.members = 'Two'

>>> m1.members                         'Two'

>>> m2.members                            2

新numbers值被写到了m1的特性中,屏蔽了类范围内的变量

2.5指定超类

子类可以扩展超类的定义,将其他类名写在class语句后的圆括号内可以指定超类:

class Filter:

      def  init(self):

          self.blocked = []

      def  filter(self, sequence):

          return [x for x in sequence if x not in self.blocked]

class SpamFilter(Filter):

      def  init(self):

           self.blocked = ['SPAM']

Filter是个用于过滤序列的通用类,事实上不能过滤任何东西

>>> f = Filter()

>>> f.init()

>>> f.filter([1,2,3]}                        [1,2,3]

Filter类的用处在于它可以用作其他类的基类(超类),比如SPAMFilter类,可以将序列中的”SPAM"过滤出去

>>> s = SPAMFilter()

>>> s.init()

>>> s.filter(['SPAM', 'SPAM', 'SPAM', 'SPAM', 'eggs', 'bacon', 'SPAM',])

['eggs', 'bacon']

SPAMFilter定义的两个要点:

这里用提供新定义的方式重写了Filter的init定义

filter方法的定义是从Filter类中拿过来(继承)的,所以不用重写它的定义

2.6调查继承

如果想要查看一个类是否是另一个的子类,可以使用内建的issubclass函数

>>> issubclass(SPAMFilter, Filter)                 True

>>> issubclass(Filter, SPAMFilter)                 False

如果想要知道已知类的基类(们),可以直接使用它的特殊特性__bases__

>>> SPAMFilter.__bases__

{.}

>>> Filter.__bases__                              {}

同样还能用使用isinstance方法检查一个对象是否是一个类的实例:

>>> s = SPAMFilter()

>>> isinstance(s, SPAMFilter)                  True

>>> ininstance(s, Filter)                             True

>>> isinstance(s, str)                                  False

可以看到s是SPAMFilter类的(直接)成员,但他也是Filter类的间接成员,因为SPAMFilter是Filter的子类

另外一种说法就是SPAMFilter类就是Filters类,可以从前一个例子中看到,

isinstance对于类型也起作用,比如字符串类型str

如果只想知道一个对象属于哪个类,可以使用__class__特性:

>>> s.__class__

如果使用__metaclass__=type或从object继承的方式来定义新式类,那么可以使用type(s)查看实例的类

2.7多个超类

class Calculator:

      def  calculate(self, expression):

           self.value = eva(expression)

class Talker:

      def talk(self):
           print 'Hi, my value is '. self.value

class TalkingCalculator(Calculator, Talker):

      pass

子类(TalkingCalculator)自己不做任何事,它从超类继承所有行为,这样就变成了会说话的计算器

>>> tc = TalkingCalculator()

>>> tc.calculate('1+2*3')

>>> tc.talk()                  Hi, my value is 7

这种行为称为多重继承,应尽量避免使用除非特别熟悉

如果一个方法从多个超类继承(即有两个具有相同名字的不同方法),必须注意超类的顺序:

先继承的类中的方法会重写后继承的类中的方法(使其不可访问)

如果超类们共享一个超类,那么在查找给定方法或者特性时访问超类的顺序成为MRO(Method Resolution Order,方法判定顺序)

2.8接口和内省

接口的概念与多态有关,在处理多态对象时,只要关心它的接口(或称协议)即可,也就是公开的方法和特性

在python中不用显示地指定对象必须包含哪些方法才能作为参数接受,例如,不用(像在Java中)显示地编写接口,可以在

使用对象的时候假定它可以实现你所要求的行为,如果不能实现的话,程序就会失败。

一般来说只需让对象符合当前的接口(实现当前方法),但是还可以更灵活一些

除了调用方法然后期待一切顺利之外,还可检查所需方法是否已经存在,如果不存在,就需要做其他事情

>>> hashttr(tc, 'talk')            True

>>>hasattr(tc, 'fnord')          False

上面的代码中,tc有个叫做talk的特性(包含一个方法),但是并没有fnord特性,如果需要话甚至还能检查talk特性是否可调用

>>> callable(getattr(tc, 'talk', None)           True

>>> callable(getattr(tc, 'fnord', None)         False

这段代码使用了getattr函数,而没有在if语句内使用hasattr函数直接访问特性,getattr函数允许提供默认值(本例中为None),以便在特性不存在时使用,然后对返回的对象使用callable函数

与getattr相对应的函数是setattr,可以用来设置对象的特性

>>> setattr(tc, 'name', 'Mr.Gumby')

>>> tc.name   'Mr.Gumby'

如果要查看对象内所有存储的值,可以使用__dict__特性

如果真的想要找到对象是由什么组成,可以看看inspect模块,

这是为哪些想要编写对象浏览器)以图形方式浏览python对象的程序)以及其他需要类似功能的程序的高级用户准备的













你可能感兴趣的:(Python)