# 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中,所有的特性都是公开可用的,但是程序员应该在直接访问对象状态时谨慎行事,因为他们可能无意中使得这些特性在某些方面不一致。
#继承
#一个类可以是一个或者多个类的子类。子类从超类继承所有方法。可以使用多个超类,这个特性可以用来组成功能的正交部分(没有任何联系)。普通的实现方式是使用核心的超类和一个或者多个混合的超类。
#接口和内省
#一般来说,对于对象不用探讨过深。程序猿可以靠多态调用自己需要的方法。不过如果想要知道对象到底有什么方法和特性,有些函数可以帮助完成这项工作。
#面向对象设计
#关于如何(或者说是否应该进行)面向对象设计有很多的观点。不管你持什么观点,完全理解这个问题,并且创建容易理解的设计是很重要的。