我的Python3学习笔记(3):面向对象

来源:互联网 发布:语嫣 淘宝网总裁 编辑:程序博客网 时间:2024/06/05 14:09

面向对象

模块

导入

Python导入有两种形式

一是importmodule,这种形式还得写全名。不同于Java

例如:

import cmathcmath.sqrt

另一种是

from module import function.from somemodule import somefunction, anotherfunction, yetanotherfunction

这种情况下就可以直接写函数名。

模块别名

>>>import math as foobar>>>foobar.sqrt(4)2.0


函数别名

>>>from math import sqrt as foobar>>>foobar(4)2.0

重新加载

importimpreload(module)


类创建

__metaclass__= type # Make sure we get new style classes classPerson:       def setName(self, name):              self.name = name       def getName(self):              return self.name       def greet(self):              print "Hello, world! I'm%s." % self.name

 

1,最上面的赋值是用来把这个类变成Python新式类,另外也可以继承一个新式类来把这个类变成新式类。Python 3中都是新式类,因此没有这个问题。

例如:

class Person(object)

2,每一个实例方法第一个参数必须是self。

3,实例变量加入对象的规则是:第一次访问这个变量时,把这个变量加入这个类中。动态语言中的类就像原型编程中的对象槽。比如这个里面没有任何的实例变量的声明。

构造方法

class ClassName(object):       """docstring forClassName"""       def __init__(self, arg):              super(ClassName, self).__init__()              self.arg = arg

 

1,如同Java一样,Python覆盖一个有参数的构造方法的时候必须显示调用父类的构造方法。使用super。

2,注意形式:这个函数前后都是两个下划线。

构造器重载

Python不支持方法重载,因为Python是动态类型语言,所以方法重载可以通过缺省参数值来实现。如果真要实现方法重载,可以使用多参数。

调用父类构造器

使用非绑定方法

class SongBird(Bird):       def __init__(self):              Bird.__init__(self)              self.sound = 'Squawk!'       def sing(self):              print self.sound

 

这里直接调用了父类的构造器,但是由于这个时候并没有绑定任何的实例,因此这个时候可以提供子类对象self。也就是说吧父类的构造器绑定到子类对象上面。

Super

class ClassName(object):       """docstring forClassName"""       def __init__(self, arg):              super(ClassName, self).__init__()              self.arg = arg


这种方法首先给super绑定子类和子类对象,然后就可以调用init方法。

访问控制

Python中使用前缀来表示其访问控制,不以下划线开头的实例变量都是共有的。

>>>foo.name'LukeSkywalker'>>>bar.name = 'Yoda'>>>bar.greet()Hello, world! I'm Yoda.

私有化

一个类中的实例变量如果是双下划线开头的,那么这个实例变量就是私有的,访问他们会出错。

class Secretive:       def __inaccessible(self):              print "Bet you can't seeme..."       def accessible(self):              print "The secret messageis:"              self.__inaccessible()

getter和setter

使用描述器

>>>class Rectangle(object):    def __init__(self):        self.width = 0        self.height = 0    def setSize(self, size):        self.width, self.height = size    def getSize(self):        return self.width, self.height    size = property(getSize, setSize)    >>>r = Rectangle()>>>r.width = 2>>>r.height = 5>>>r.size(2,5)>>>r.size = 10,20>>>r.size(10,20) 


这种方法就是结合访问器方法和property函数来实现一个属性的getter和setter,如果属性是私有的。那么size就可以以公有的形式来访问了。

实现方法

class Rectangle(object):    def __init__(self):        self.width = 0        self.height = 0    def setSize(self, size):        self.width, self.height = size    def getSize(self):        return self.width, self.height    size = property(getSize, setSize)


 

在这个代码中实际上使用了描述器,一个描述器就是一个具有__get__和__set__的类。这样对于这个对象的任何任何的访问都会把它转义到这个描述器中的__get__和__set__方法中。

这儿property其实是一个类,

def prop(getter, setter, deleter):    class PropDesc:        def __get__(self, instance, owner):            return getter(instance)        def __set__(self, instance, value):            setter(instance, value)        def __delete__(self, instance):            deleter(instance)               return PropDesc()


可以看到这个函数返回了一个对象,这个对象就是一个描述符,借助于描述符的简便的访问规则。这个属性访问的时候就想一个普通属性那样自然。

使用修饰器

class Ball:    def __init__(self, radius):        if radius <= 0:            raise ValueError('必須是正數')        self.__radius = radius       @property    def radius(self):        return self.__radius           @radius.setter    def radius(self, radius):        self.__radius = radius       @radius.deleter    def radius(self):        del self.__radius


 

根据修饰器的规则:property是一个函数,因此在@property出现的地方其实是创建了一个对象radius,然后后面的两个修饰器则是使用了这个对象,并且把他们变成了property的一个函数。

实例方法的动态性

1,一个实例方法既可以被重新赋值>>>class Class:                     def method(self):                            print 'I have aself!'>>>def function():                     print "Idon't...">>>instance = Class()>>>instance.method()Ihave a self!>>>instance.method = function>>>instance.method()Idon't...


2,实例方法也可以赋值给其他的方法

>>>class Bird:       song = 'Squaawk!'       def sing(self):              print self.song>>>bird = Bird()>>>bird.sing()Squaawk!>>>birdsong = bird.sing>>>birdsong()Squaawk!


类方法

1)静态方法无需传入self参数,类成员方法需传入代表本类的cls参数;

2)从第1条,静态方法是无法访问实例变量的,而类成员方法也同样无法访问实例变量,但可以访问类变量;

3)静态方法有点像函数工具库的作用,而类成员方法则更接近类似Java面向对象概念中的静态方法,也就是说静态方法使用的使用Python类的命名空间的作用。

例如:

class MyClass:       @staticmethod       def smeth():              print 'This is a static method'       @classmethod       def cmeth(cls):              print 'This is a class method of',cls 


4)。同Java,Python中的类方法不会被继承。

类变量

在类体中声明的变量就是类变量。类变量和Java中的类变类一致,但是Python中比较混淆。尤其是类变量必须通过self来访问,也可以使用类名来引用。

注意:在实例方法中调用类变量的时,必须加上self。

 

例如:

>>>class Bird:       song = 'Squaawk!' #1       def sing(self):              print self.song #2>>>bird = Bird()>>>bird.sing()Squaawk!>>>Bird.sing()


 

类变量在类本身和类实例间共享。

class MemberCounter:       members = 0       def init(self):              MemberCounter.members += 1>>>m1 = MemberCounter()>>>m1.init()>>>MemberCounter.members1>>>m2 = MemberCounter()>>>m2.init()>>>MemberCounter.members2


调用

类层次方法既可以用对象引用来调用它,也可以使用类来调用调用结果都是一样的。

魔法方法

序列和字典

字典和序列都是根据一个规则来声明的,也就是按照鸭子类型的说法:如果他有鸭子的行为,那么他就是一个鸭子。如果实现下面的方法,那么这个类就是一个序列

1,__len__

返回这个集合中元素的个数,如果返回值是0,那么表示这个集合没有任何元素,并且在boolean环境下他表示的是False。

2,__getitem__(self,key)

得到指定键的值。对于序列来说他可能是一个数值,对于一个字典来说这个键可能是任何可以作为键的值。

3,__setitem__(self,key,value)

同上,只是这个是用来设置这个序列的一个值。

4,__delitem__(self,key)

这个方法用来删除一个元素。

Python对象属性

dict字典

这些对象的属性方法都是默认和Python对象中的__dict__打交道,也就是说他们都是在操作__dict__这个字典。

 

例如:            

>>>class Foo(object):...     def __getattr__(self, name):...         return "Value of %s" % name>>>foo = Foo()>>>foo.just_this = "Some value">>>foo.just_this'Somevalue'>>>foo.something_else'Value ofsomething_else'


__getattribute__(self,name)

Python2.2 及之后版本中,您可以选择使用 .__getattribute__() 方法,代替具有类似名称且易被混淆的老式.__getattr__() 方法。如果使用的是新式的类(一般情况下总是如此),您就可以这样做。.__getattribute__()方法比它的同类方法更为强大,因为不管属性是不是在 obj.__dict__ 或 obj.__slots__ 中定义的,它将拦截所有 属性访问。使用.__getattribute__() 方法的一个缺点是,所有访问都需通过该方法。如果您使用这种方法,并希望返回(或操作)属性的 “real” 值,则需要进行少量特殊的编程:通常可通过对超类(一般为 object)调用 .__getattribute__() 实现。例如:

 

>>>class Foo4(object):...     def __getattribute__(self, name):...         try:...             returnobject.__getattribute__(self, name)...         except:...             return "Value of %s" %name...>>>foo4 = Foo4()>>>foo4.x = 'x'>>>foo4.x'x'>>>foo4.y'Value of y'


__getattr__(self,name)

如果把一个对象看做一个属性槽,那么这个就是用来得到name的属性。不同于__setattr__的是__getattr__值拦截没有在对象中定义的属性。也就是说__getattr__是用来收拾烂摊子的。

 

class Foo(object):       def __init__(self):              self.name="ssj"        def __getattr__(self, name):              return "Value of %s" %name >>>import Main>>>foo = Main.Foo>>>foo = Main.Foo()>>>foo.name'ssj'>>>foo.time'Valueof time'>>> 


__setattr__(self,name)

 

class Rectangle:       def __init__(self):              self.width = 0              self.height = 0       def __setattr__(self, name, value):              if name == 'size':                     self.width, self.height =value              else:                     self.__dict__[name] = value       def __getattr__(self, name):              if name == 'size':                     return self.width,self.height              else:                     raise AttributeError


 

__setattr__会拦截所有的属性访问请求,因此:不能仅仅关注访问的属性是size的时候,还有注意如果属性不是size,那么把他放到这个对象的属性槽里面。如果不实现加入__dict__这一步,甚至连实例属性都没法声明,因为每次要设定实例属性的时候都会访问__setattr__,但是他没有实现任何的方法,因此会抛出异常。

class Foo(object):       def __init__(self):              self.name="ssj"        def __getattr__(self, name):              return "Value of %s" %name        def __setattr__(self,name,value):              print("__setattr__ in theway!!!")


 

结果:

>>>foo = Main.Foo()__setattr__in the way!!!>>>foo.name'Valueof name'>>>foo.name = "ssj"__setattr__in the way!!!>>>foo.name'Valueof name'


可以看到name这个属性并没有在对象中,因为__setattr__被覆盖。

__delattr__(self,name)

删除一个属性。

hasattr函数

这个函数和__getattr__协同工作,当__getattr__放回值时,那么判断为True,如果__getattr_抛出AttributeError,那么结果为False。但是如果抛出其他异常hasattr也会抛出异常。

默认情况

__slots__

这个类中的元组规定了可以加入对象__dict__的名称,也就是说只有在

__slots__中出现的属性名称才能加入到__dict__中。

 

>>>class Foo2(object):...     __slots__ = ('just_this')...     def __getattr__(self, name):...         return "Value of %s" % name>>>foo2 = Foo2()>>>foo2.just_this = "I'm slotted">>>foo2.just_this"I'mslotted">>>foo2.something_else = "I'm not slotted"AttributeError:'Foo' object has no attribute 'something_else'>>>foo2.something_else'Valueof something_else'


可以看到,这个并没有影响__getattr__的使用,仅仅是使得__setattr__的执行不成功。

结合使用 .__setattr__ 和 .__slots__

>>>class Foo3(object):...     __slots__ = ('x')...     def __setattr__(self, name, val):...         if name in Foo.__slots__:...             object.__setattr__(self, name,val)...     def __getattr__(self, name):...         return "Value of %s" % name...>>>foo3 = Foo3()>>>foo3.x'Valueof x'>>>foo3.x = 'x'>>>foo3.x'x'>>>foo3.y'Valueof y'>>>foo3.y = 'y'   # Doesn't do anything, butdoesn't raise exception>>>foo3.y'Value of y'


迭代器

迭代器是一个有next方法的对象(在Python3中迭代器对象应该实现__next__方法)。迭代器对象应该每次放回下一个对象,如果没有下一个对象放回,那么应该抛出StopIteration异常表示结束。

 

一个类的__iter__方法用来放回迭代器。

如:

classFibs:       def __init__(self):              self.a = 0              self.b = 1       def next(self):              self.a, self.b = self.b,self.a+self.b              return self.a       def __iter__(self):              return self


迭代器到列表

调用list方法可以把一个迭代器装换成列表。

>>>class TestIterator:       value = 0       def next(self):              self.value += 1              if self.value > 10: raise StopIteration              return self.value       def __iter__(self):              return self              ...>>>ti = TestIterator()>>>list(ti)[1, 2, 3, 4, 5, 6, 7, 8,9, 10]


描述器

描述符是吧一个对象变成一个操作和基本类型相同的方法。因此这样的对象既可以像普通对象那样访问,又可以借组于强大的类定义各种使用这个对象的方法。得到的结果就是一个描述符。

 

例如:

class Descriptor:    def __get__(self, instance, owner):        print(self, instance, owner)    def __set__(self, instance, value):        print(self, instance, value)    def __delete__(self, instance):        print(self, instance) class Some:    x = Descriptor()

 

使用:

s =Some()

s.x

s.x= 10

dels.x

传统的对象都是用来引用他的属性的,但是这儿的x虽然也是一个对象,但是他的使用就好像一个基本类型的实例变量一样。

这些对于x的访问其实就是

s =Some()Some.__dict__['x'].__get__(s,  Some);Some.__dict__['x'].__set__(s,  10);Some.__dict__['x'].__delete__(s);

描述器和__getattr__类方法的区别

描述器是用来规定一个类的对象本身被存取的时候所发生的动作。而__getattr__则规定的是一个类的实例属性访问的时候的动作。比如一个对象如果同时定义了这两个方法的时候.

class Difference(object):    def __get__(self, instance, owner):        print("__get__")     def __set__(self, instance, value):        print("__set__")     def __delete__(self, instance):        print("__delete__")     def __getattr__(self,name):        print("__getattr__")     def __setattr__(self,name,value):        print("__setattr__") class MyWapper(object):    d = Difference() m =MyWapper()m.dm.d= 10delm.dm.d.sjsjjsm.d.sjsjshs=10delm.d.sjhjhshjs结果__get____set____delete____get__


__call__()

__call__方法就是让一个对象成为可执行的,也就是说可以按照函数那样的语法来调用。

class Some:    def __call__(self, *args):        for arg in args:            print(arg, end=' ')        print() s =Some()s(1)s(1,2)s(1, 2, 3)


继承内建类

在新版Python中可以继承内建类,这样就可以方便的建立一个列表或者字典了。

class CounterList(list):       def __init__(self, *args):              super(CounterList,self).__init__(*args)              self.counter = 0       def __getitem__(self, index):              self.counter += 1              return super(CounterList,self).__getitem__(index)


Monkey Patch

由于动态语言中的对象个作用域的动态性,我们可以在代码中动态的改变一个属性,方法或者函数。事实上在Ruby,Python和其他的动态语言中。对象和作用域都是一个属性槽。因此改变他们也没什么难的。

继承

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): # SPAMFilter is a subclass of Filterdef __init__(self): # Overrides init method from Filter superclass       self.blocked = ['SPAM']


多继承

一个类可以继承多个父类,如果多个方法有重名。那么使用在前面的类的方法。

对象创建

>>>foo = Person()>>>bar = Person()>>>foo.setName('Luke Skywalker')>>>bar.setName('Anakin Skywalker')>>>foo.greet()Hello,world! I'm Luke Skywalker.>>>bar.greet()Hello, world! I'm AnakinSkywalker.


异常

自定义异常类

class SomeCustomeException(Exception):pass;


捕获异常

try:       x = input('Enter the first number: ')       y = input('Enter the second number: ')       print x/yexceptZeroDivisionError:       print "The second number can't bezero!"


Python中的异常没有参数,如果要重新抛出,那么直接使用raise就可以:

class MuffledCalculator:       muffled = False       def calc(self, expr):              try:                     return eval(expr)              except ZeroDivisionError:                     if self.muffled:                            print 'Division byzero is illegal'                     else:                            raise


多个Except

try:       x = input('Enter the first number: ')       y = input('Enter the second number: ')       print x/yexcept ZeroDivisionError:       print "The second number can't bezero!"except TypeError:       print "That wasn't a number, was it?"


多个Exception

try:       x = input('Enter the first number: ')       y = input('Enter the second number: ')       print x/yexcept(ZeroDivisionError, TypeError, NameError):       print 'Your numbers were bogus...'


捕获异常对象

try:       x = input('Enter the first number: ')       y = input('Enter the second number: ')       print x/yexcept(ZeroDivisionError, TypeError), e:       print e


全捕获

try:       x = input('Enter the first number: ')       y = input('Enter the second number: ')       print x/yexcept:       print 'Something wrong happened...'


else子句

while True:       try:              x = input('Enter the first number:')              y = input('Enter the secondnumber: ')              value = x/y              print 'x/y is', value       except:              print 'Invalid input. Please tryagain.'       else:              break


finally子句

try:       1/0except NameError:       print "Unknown variable"else:       print "That went well!"finally:       print "Cleaning up."