下面开始是进阶部分了。
四、类
1. 类
在Python2.2之前使用的是旧式类,2.2版本之后使用的是新式类,但是在Python3之后就不存在这个问题了。下面谈论的问题是关于新式类的。
关于新式类的定义有两种方式。
1)继承性定义。
<span style="font-size:18px;">class A(object): pass a = A() print a.__class__ #<class '__main__.A'> print type(a) #<class '__main__.A'></span>2)在类的前面写上这么一句:__metaclass__ == type(表示下面定义的类是新式类),然后定义类的时候,就不需要在名字后面写(object)了。
<span style="font-size:18px;">__metaclass__ = type class A: pass a = A() print a.__class__ #<class '__main__.A'> print type(a) #<class '__main__.A'></span>下面介绍了创建一个新的类。类中的方法的参数必须包括self参数,并且作为默认的第一个参数。def __init__叫做初始化函数。
<span style="font-size:18px;">__metaclass__ = type class Person: def __init__(self,name): self.name = name def getName(self): return self.name def age(self,age): print "%s is %d years old" % (self.name,age) p = Person('why') print p.getName() #why p.age(22.5) #why is 22 years old </span>
当类中变量引用的是不可变数据时,实例属性不会影响类属性,而类属性会影响实例属性。当类中变量引用的是可变对象时,类属性和实例属性都能直接修改这个对象,从而影响另一方的值。
<span style="font-size:18px;">class A(object): x = 3 y = [1,2,3] a = A() a.x = 4 print A.x #3 print a.x #4 a.y.append(4) print A.y #[1, 2, 3, 4] print a.y #[1, 2, 3, 4]</span>在类确定或者实例化之后,也可以增加和修改属性,其方法就是通过类或者实例的点号操作来实现,即object.attribute,可以实现对属性的修改和增加。但是增加实例属性,类属性不会变。
<span style="font-size:18px;">class A(object): x = 3 y = [1,2,3] a = A() a.z = 'why' print a.z #why print A.z #AttributeError: type object 'A' has no attribute 'z' A.q = 22.5 print a.q #22.5 print A.q #22.5</span>
命名空间因为对象的不同,也有所区别,可以分为如下几种:
1)内置命名空间(Built-in Namespaces):Python运行起来,它们就存在了。内置函数的命名空间都属于内置命名空间,所以,我们可以在任何程序中直接运行它们,比如d(),不需要做什么操作,拿过来就直接使用了。
2)全局命名空间(Module:Global Namespaces):每个模块创建它自己所拥有的全局命名空间,不同模块的全局命名空间彼此独立,不同模块中相同名称的命名空间,也会因为模块的不同而不相互干扰。
3)本地命名空间(Function&Class: Local Namespaces):模块中有函数或者类,每个函数或者类所定义的命名空间就是本地命名空间。如果函数返回了结果或者抛出异常,则本地命名空间也结束了。
此外,还谈到了数据轮转和作用域的问题。
下面是谈了类的继承和方法的重写,由于和JAVA差不多,所以不再赘述。
在旧式类中多重继承的顺讯是依照深度优先来的,而在新式类中是按照广度优先来的。类的__mro__属性可以得到类的继承顺序。
<span style="font-size:18px;">class A(object):</span>
<span style="font-size:18px;"> def foo(self): print "foo A" class B(object): def foo(self): print "foo B" def bar(self): print "bar B" class C(A,B): pass class D(A,B): def bar(self): print "bar D" class E(C,D): pass print E.__mro__ #(<class '__main__.E'>, <class '__main__.C'>, <class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>) e = E() e.foo() #foo A e.bar() #bar D</span>在新式类中如果要调用父类的方法,可使用super函数,尤其是在初始化函数的时候。super函数的参数,第一个是当前子类的类名字,第二个是self,然后是点号,点号后面是所要调用的父类的方法。
<span style="font-size:18px;">__metaclass__ = type class Person: def __init__(self): self.name = 'why' def p(self): print "My name is {}".format(self.name) class Employer(Person): def __init__(self): super(Employer,self).__init__() self.age = 22.5 def p(self): print "I am {} years old".format(self.age) super(Employer,self).p() e = Employer() e.p() # I am 22.5 years old # My name is why</span>
在python中:@staticmethod表示下面的方法是静态方法,@classmethod表示下面的方法是类方法。静态方法依然用def语句来定义。需要注意的是文件名后面的括号内没有self,那么也就无法访问实例变量、类和实例的属性了,因为它们都是借助self来传递数据的。类方法的参数也没有self,但是必须有cls这个参数。在类方法中,能够访问类属性,但是不能访问实例属性。两种方法都可以通过实例调用,即绑定实例。也可以通过类来调用。关于两者的差异看以参见这篇文章PYTHON中STATICMETHOD和CLASSMETHOD的差异。
在函数、类或者文件开头的部分写文档字符串说明,一般采用三重引号。这样写的最大好处是能够用help()函数看。
<span style="font-size:18px;">"""This is python lesson""" def start_func(arg): """This is a function.""" pass class MyClass: """This is my class.""" def my_method(self,arg): """This is my method.""" pass</span>
2. 多态和封装
count()函数的作用是数一数某个元素在对象中出现的次数。
<span style="font-size:18px;">print "wwhhy".count('w') #2 print [1,1,2,3,4].count(2) #1</span>repr()函数能够针对输入的任何对象返回一个字符串。用它可以体现出多态。
<span style="font-size:18px;"><span style="font-size:18px;">def length(x): print "the length of " , repr(x) , "is ", len(x) length([1,2,3]) #the length of [1, 2, 3] is 3 length('why') #the length of 'why' is 3 length({1:'why',2:'zmq'}) #the length of {1: 'why', 2: 'zmq'} is 2</span></span>python中私有化的方法也比较简单,就是在准备私有化的属性(包括方法、数据)名字前面加双下划线。如果要调用那些私有属性可以使用property函数。用了@property之后,在调用那个方法的时候,用的是p.xx的形式,就好像在调用一个属性一样,
<span style="font-size:18px;">__metaclass__ = type class A: def __init__(self): self.me = "why" self.__name = "zmq" @property def name(self): return self.__name if __name__ == "__main__": p = A() print p.name #zmq</span>
3. 特殊方法
接下来介绍了几个特殊方法:
1)__dict__:保存了对象的属性。下面是一个综合的例子,属性和方法的情况都类似。
<span style="font-size:18px;">class A(object): name = 'why' print A.__dict__ #{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, 'name': 'why', '__doc__': None} print A.__dict__['name'] #why print A.name #why a = A() print a.__dict__ #{} print a.name #why a.name = 'zmq' print a.__dict__ #{'name': 'zmq'} print a.__dict__['name'] #zmq print a.name #zmq print A.__dict__ #{'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, 'name': 'why', '__doc__': None} print A.__dict__['name'] #why print A.name #why del a.name print a.__dict__ #{} print a.name #why a.age = 22.5 print a.__dict__ #{'age': 22.5} print a.age #22.5 print A.__dict__['age'] #KeyError: 'age' print A.age #AttributeError: type object 'A' has no attribute 'age' A.major = 'computer' print a.__dict__ #{'age': 22.5} print a.major #computer</span>2)_slots__:能够限制属性的定义,在编程中非常重要的方面是优化内存使用。_slots__已经把实例属性牢牢地管控了起来,但更本质是的是优化了内存。这种优化会在大量的实例时候显出效果。
<span style="font-size:18px;">class A(object): __slots__ = ('name','age') print A.__slots__ #('name', 'age') a = A() print a.__slots__ #('name', 'age') A.name = 'why' print a.name #why # a.name = 'zmq' #AttributeError: 'A' object attribute 'name' is read-only a.age = 22.5 print a.age #22.5 print A.age #<member 'age' of 'A' objects> A.age = 23 print a.age #23 a.major = 'computer' #AttributeError: 'A' object has no attribute 'major'</span>3)__setattr__(self,name,value):如果要给name赋值,就调用这个方法。
4)__getattr__(self,name):如果name被访问,同时它不存在的时候,此方法被调用。
<span style="font-size:18px;">class A(object): def __getattr__(self, name): print "You use getattr" def __setattr__(self, name, value): print "You use setattr" self.__dict__[name] = value a = A() a.x #You use getattr a.x = 3 #You use setattr print a.x #3</span>
5)__getattribute__(self,name):当name被访问时自动被调用(注意:这个仅能用于新式类),无论name是否存在,都要被调用。
6)__delattr__(self,name):如果要删除name,这个方法就被调用。
<span style="font-size:18px;">class B(object): def __getattribute__(self, name): print "you are useing getattribute" return object.__getattribute__(self, name) b = B() b.y #you are useing getattribute AttributeError: 'B' object has no attribute 'y' b.y = 4 print b.y #4</span>通过实例获取其属性,如果在__dict__中有相应的属性,就直接返回其结果;如果没有,会到类属性中找。如果没有定义__getattr__()方法,就会引发AttributeError。
<span style="font-size:18px;">__metaclass = type class A: name = 'why' def __getattr__(self,key): if key != 'name': return "check again" a = A() print a.name #why print a.major #check again</span>4. 迭代器
<span style="font-size:18px;">__metaclass__ = type class A: def __init__(self,n): self.i = 0 self.n = n def __iter__(self): return self def next(self): if self.i < self.n: i = self.i self.i += 1 return i else: raise StopIteration() if __name__ == "__main__": a = A(7) print a.next() print a.next() print "------------" for i in a: print i # 0 # 1 # ------------ # 2 # 3 # 4 # 5 # 6</span>
关于列表和迭代器之间的区别,还有两个非常典型的内建函数:range()和xrange()。range()返回的是一个列表,xrange()返回的是一个对象,在循环的时候稍快并有更高的内存效率。即通过range()得到的列表,会一次性被读入内存,而xrange()返回的对象,则是需要一个数值才从返回一个数值。下面的例子将会体现xrange()函数的优势。
<span style="font-size:18px;">__metaclass__ = type a = range(4) b = xrange(10000) print zip(a,b) 3[(0, 0), (1, 1), (2, 2), (3, 3)]</span>5. 生成器
<span style="font-size:18px;"><span style="font-size:18px;">a = (i**2 for i in range(4)) for i in a: print i, #0 1 4 9 print "" for i in a: print i, #</span></span>yeild关键词是生成器的标识。函数返回值是一个生成器类型的对象,这个生成器对象就是迭代器。我们把含有yield语句的函数称作生成器。生成器是一种用普通函数语法定义的迭代器。
<span style="font-size:18px;">def a(): yield 0 yield 1 yield 2 a = a() print a.next() #0 print a.next() #1 print a.next() #2 print a.next() #StopIteration </span>发现yield除了作为生成器的标志之外,还有一个功能就是返回值。return会结束函数体的执行,而yeild则会暂时挂起,等到下次调用next方法时再继续。
<span style="font-size:18px;">def a(): print "before" for i in range(4): return i print "after" aa = a() #before print aa #0 print aa #0 def b(): print "before" for i in range(4): yield i print "after" bb = b() #before print bb.next() #0 print bb.next() #after 1</span>
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/u012421846/article/details/47092717