# coding:utf-8

#--------------------------------------------------  对象魔力  -----------------------------------------

#多态

#多态方法

print 'abc'.count('a')

#1

print [1, 2, 'a'].count('a')

#1

#不用关心count('a')里面是什么类型数据,只要知道count方法,可以返回a出现多少次,就行了,这就是多态方法

#choice函数,可以从序列中随机选出元素。

from random import choice

x = choice(['Hello, world!', [1, 2, 'e', 'e', 4]])

#x可能是Hello,world字符串,可能是[1, 2, 'e', 'e', 4]列表,不用关心到底是哪个类型,只要关心变量x中字符e出现了多少次。不管x是字符串还是列表,都可以使用count函数。

print x.count('e')

#2              #本例中,看来列表胜出了。

#多态多种形式

#当面对对象不知道是什么类型,但又要对对象做些什么时候,就会用到多态

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

print 1 + 2

#3

print 'Fish' + 'license'

#Fishlicense

#这里的加运算符对于数字和字符串都能起作用。

#为说明这一点,假设有个叫做add的函数,它可以将两个对象相加。可以直接将其定义成上面的形式。

def add(x, y):

   return x+y

#对于很多类型的参数都可以用:

print add(1, 2)

#3

print add('Fish', 'license')

#Fishlicense

#如果需要编写打印对象长度消息的函数,只需对象具有长度(len函数可用)即可。

def length_message(x):

   print "The length of", repr(x), "is", len(x)

#可以看到,函数中还使用了repr函数,repr函数是多态特性。

length_message('Fnord')

#The length of 'Fnord' is 5

length_message([1, 2, 3])

#The length of [1, 2, 3] is 3

#很多函数和运算符都是多态的---你写的绝多数程序可能都是,即便你并非有意这样。只要使用多态函数和运算符,就会与“多态”发生关联。

#事实上,唯一能毁掉多态的就是使用函数检查类型,比如type,isinstance以及issubclass函数等。

#如果可能的话,应该尽力避免使用这些毁掉多态的方式。真正重要的是如何让对象按照你所希望的方式工作,不管它是否是正确的类型(或者类)。

#封装

#封装是指向程序中的其他部分隐藏对象的具体实现细节原则。

#接下面再论私有化进行详细解释

#继承

#就是函数,类等调用,相同代码不想重复写,直接调用给其他代码,就是继承。

#--------------------------------------------  类和类型 ------------------------------------------

#创建类

__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

#这个例子包含3个方法定义,除了它们是写在class语句里,一切都像是函数定义。Person是类的名字。class语句会在函数定义的地方创建自己的命名空间。

#self参数看起来有点奇怪,它是对于对象自身的引用。那么它是什么对象?创建一些实例看看:

foo = Person()

bar = Person()

foo.setName('Luke Skywalker')               #foo将自己为第一个参数传入到self里

bar.setName('Anakin Skywalker')

foo.greet()

#Hello, world! I'm Luke Skywalker.

bar.greet()

#Hello, world! I'm Anakin Skywalker.

#在调用foo的setName和greet函数时,foo自动将自己作为第一个参数传入函数中---因此形象地命名为self。对于这个变量,每个人可能都会有自己的叫法,但是因为它总是对象自己,所以习惯上总是叫做self

#显然这就是self的用处和存在的必要性。没有它的话,成员方法就没法访问它们要对其特性进行操作的对象本身了。

#特性可以在外部访问:

#就是setName函数里的self.name = name

print foo.name

#Luke Skywalker

bar.name = 'Yoda'

bar.greet()

#Hello, world! I'm Yoda.

#如果知道foo是Person的实例的话,那么可以把foo.greet()写成:

Person.greet(foo)           #方便的简写

#Hello, world! I'm Luke Skywalker.

#特性,函数,方法

__metaclass__ = type

class Class:

   def method(self):

       print 'I have a self!'

def function():

       print "I don't..."

instance = Class()              #调用类

instance.method()               #instance是实例,method是方法,有self参数

#I have a self!

instance.method = function      #绑定到一个普通函数上,没有self参数

instance.method()

#I don't...

__metaclass__ = type

class Bird:

   song = 'Squaawk!'

   def sing(self):

       print self.song

bird = Bird()

bird.sing()

#Squaawk!

birdsong = bird.sing

birdsong()              #调用方法和函数十分相似,但变量绑定方法bird.sing还是会对self参数进行访问

#Squaawk!

#再论私有化,接上面的封装

#使用上面类的例子:

__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

o = Person()            #调用类

o.setName('Sir Lancelot')

print o.getName()

#Sir Lancelot

#变量o绑定到对象(Person类)上,可以使用setName和getName方法。一切看起来很完美。但是假设变量o将它的名字存储在全局变量globalName中:

#但假设变量o将它的名字存储在全局变量globalName中:

#globalName

#'Sir Lancelot'

#这意味着在使用OpenObject类的实例时候,不得不关心globalName的内容。实际上要确保不会对它进行任何更改:

#globalName = 'Sir Gumby'

#o.getName()

#'Sir Gumby'

#如果创建了多个OpenObject实例的话就会出现问题,因为变量相同,所以可能会混淆:

#o1 = OpenObject()

#o2 = OpenObject()

#o1.setName('Robin Hood')

#o2.getName()

#'Robin Hood'

#设定一个名字后,其他名字也自动设定了。

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

__metaclass__ = type

class Secretive:

   def __inaccessible(self):            #让方法变成私有化(外部无法访问),只要在它的名字前面加上双下划线即可

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

   def accessible(self):

       print "The secret message is:"

       self.__inaccessible()           #内部调用

s = Secretive()

# s.__inaccessible()              #报错无法调用

s.accessible()                  #内部可以调用

#The secret message is:

#Bet you can`t see me...

s._Secretive__inaccessible()       #也有外部调用方法,但不应该这么做

#Bet you can`t see me...

#类的命名空间

__metaclass__ = type

class MemberCounter:

   members = 0

   def init(self):

       MemberCounter.members += 1

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

m1 = MemberCounter()        #调用类

m1.init()

print MemberCounter.members

#1

m2 = MemberCounter()

m2.init()

print MemberCounter.members

#2

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

print m1.members

#2

print m2.members

#2

m1.members = 'Two'              #新的numbers值被写到了m1特性中,屏蔽了类范围内的变量。这跟函数内的局部和全局变量的行为十分类似。

print m1.members

#Two

print m2.members

#2

#指定超类

__metaclass__ = type

class Filter:           #写一个过滤作用的类(超类)

   def init(self):

       self.blocked = []

   def filter(self, sequence):

       return [x for x in sequence if x not in self.blocked]  # 如何理解这句话,同等于下面注释

#        l = []

#        for x in sequence:

#            if x not in self.blocked:

#                l.append(x)

#        return l

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

f = Filter()

f.init()

print f.filter([1, 2, 3])

#[1, 2, 3]

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

class SPAMFilter(Filter):               #继承上面的类,可以不用写一大堆过滤的类,从上面继承更加方便。SPAMFilter是Filter的子类,Filter是SPAMFilter的超类

   def init(self):                     #重写超类Filter中的init方法

       self.blocked = ['SPAM']         #本来过滤关键字没写,现在写入SPAM

s = SPAMFilter()

s.init()

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

#['eggs', 'bacon']

#检查继承

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

print issubclass(SPAMFilter, Filter)        #查看SPAMFilter是否是Failter的子类

#True

print issubclass(Filter, SPAMFilter)

#False

#如果想要知道已知类的基类(们)(注意下节解释),可以使用特殊特性__bases__:

print SPAMFilter.__bases__       #已知类的基类,使用__bases__

#(<class '__main__.Filter'>,)

print Filter.__bases__

#(<type 'object'>,)

#使用isinstance方法检查一个对象是否是一个类的实例:

s = SPAMFilter()

print isinstance(s, SPAMFilter)

#True

print isinstance(s, Filter)

#True

print isinstance(s, str)

#False

#想知道一个对象属于哪个类,可以使用__class__特性

print s.__class__

#<class '__main__.SPAMFilter'>

#多个超类

#上节提到一个类的基类(们),也就暗示它的基类可能会多于一个。事实上就是这样,建立几个新类来试试:

class Calculator:

   def calculate(self,expression):

       self.value = eval(expression)

class Talker:

   def talk(self):

       print 'Hi, my value is', self.value

class TalkingCalculator(Calculator, Talker):

   pass

#子类(TalkingCalculator)自己不做任何事,它从自己的超类继承所有的行为。它从Calculator类那里继承calculate方法,从Talker类那里继承talk方法,这样它就成了会说话的计算器。

tc = TalkingCalculator()

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

tc.talk()

#Hi, my value is 7

#这种行为称为多重继承,是个非常有用的工具。但除非特别熟悉多重继承,否则应该尽量避免使用,因为有些时候会出现不可预见的麻烦。

#需要注意是如果一个方法从多个超类继承(也就是说有两个具有相同名字的不同方法),那么必须注意下超类的顺序(在class语句中):先继承的类中的方法会重写后继承的类中的方法。

#如果前例中Calculator:类也有个叫talk方法(函数),那么它会覆盖Talker的talk方法(使其无法访问)。如果把它们顺序掉过来,像下面这样:

#class TalkingCalculator(Talker, Calculator): pass

#就会让Talker的talk方法可用了。

#接口和内省

#可以查看方法是否存在(接口)

print hasattr(tc, 'talk')

#True

print hasattr(tc, 'fnord')

#False

#小结

#对象

#对象包括特性和方法。特性只是作为对象的一部分的变量,方法则是存储在对象内的函数。(绑定)方法和其他函数的区别在于方法总是将对象作为自己的第一个参数,这个参数一般称为self。

#类

#类代表对象的集合(或一类对象),每个对象(实例)都有一个类。类的主要任务是定义它的实例会用到方法。

#多态

#多态是实现将不同类型和类的对象进行同样对待的特性---不需要知道对象属于哪个类就能调用的方法。

#封装

#对象可以将它们的内部状态隐藏(或封装)起来。在一些语言中,这意味着对象的状态(特性)只对自己的方法可用。在Python中,所有的特性都是公开可用的,但是程序员应该在直接访问对象状态时谨慎行事,因为他们可能无意中使得这些特性在某些方面不一致。

#继承

#一个类可以是一个或者多个类的子类。子类从超类继承所有方法。可以使用多个超类,这个特性可以用来组成功能的正交部分(没有任何联系)。普通的实现方式是使用核心的超类和一个或者多个混合的超类。

#接口和内省

#一般来说,对于对象不用探讨过深。程序猿可以靠多态调用自己需要的方法。不过如果想要知道对象到底有什么方法和特性,有些函数可以帮助完成这项工作。

#面向对象设计

#关于如何(或者说是否应该进行)面向对象设计有很多的观点。不管你持什么观点,完全理解这个问题,并且创建容易理解的设计是很重要的。