Python -- 面向对象高级编程

面向对象设计中最基础的3个概念:数据封装、继承和多态

使用_ _slots_ _

     _ _slots_ _的作用:限制实例的属性

    为了达到限制的目的,在定义class时,定义一个_ _slots_ _特殊变量,来限制该class实例添加的属性:

注意:1、_ _slots_ _仅对当前类实例起作用,对继承的子类是不起作用的

           2、如果子类中也定义_ _slots_ _,那么子类实例允许添加的属性就是自身的_ _slots_ _加上父类的_ _slots_ _

           3、使用_ _slots_ _添加属性时,一定要给属性加上引号

  为了给各个实例都绑定方法,可以给class绑定方法

     通常情况下,添加方法可直接定义在类中,但动态绑定允许我们在程序运行的过程中动态给class加上功能,这在静态语言中很难实现。

 1 class Student(object):
 2     __slots__=('name','age') #用tuple定义允许绑定的属性名称
 3 
 4 s=Student() #创建一个实例
 5 s.name='Maria'
 6 s.age=23
 7 # s.score=99  由于在类Student中限制了属性,所以添加score就会报错
 8 print("s.name:",s.name)
 9 print("s.age:",s.age)
10 
11 print("给s实例添加score属性后,报错类型为:")
12 try:
13     s.score=99
14 except AttributeError as e:
15     print("AttributeError:",e)
16 
17 print('    ')
18 print("下面定义了子类:")
19 class Graduate(Student):  #继承了Student类,那么也继承了Student类的_ _slots_ _
20     __slots__=('score')  #这在子类中也定义了_ _slots_ _
21 #看出下面g.name和g.age都正常,说明子类继承了Student类的_ _slots_ _
22 g=Graduate()
23 g.score=99
24 g.name='shirley'
25 g.age=34
26 print("g.name,g.age:",g.name,g.age)
27 print("g.score:",g.score)

运行结果:

 @property

 1 class Student(object):
 2     @property
 3     def score(self):
 4         return self._score  #是self._score,不是self.score,中间的下划线不能省略
 5 
 6     @score.setter
 7     def score(self,value):   #都是raise ValueError, V和E要大写,先判断输入的数是不是Int,再判断是否在100以内
 8         if not isinstance(value,int):
 9             raise ValueError("please input an integer")
10         elif value<0 or value>100:
11             raise ValueError("请输入0到100之间的数!!!")
12         else:
13             self._score=value
14 
15 s=Student()
16 s.score=99
17 print("s.score:",s.score)
18 print("    ")
19 s1=Student()
20 s1.score=999
21 print("s1.score的分数:",s1.score)

运行结果:

 测试@property的效果:

 1 class Screen(object):
 2     __width = 0
 3 
 4     def get_width(self):
 5         return self.__width
 6 
 7     @property
 8     def width2(self):
 9         return self.__width
10 
11 s = Screen()
12 print("type(s.get_width)类型为:",type(s.get_width)) # <class 'method'>
13 print("type(s.width2)类型为:",type(s.width2)) # <class 'int'>

运行结果:

实战:请利用@property给一个Screen对象加上width和height属性,以及一个只读属性resolution:

 1 class Screen(object):
 2     @property
 3     def width(self):
 4         return self._width
 5     @width.setter
 6     def width(self,value):
 7          self._width =value
 8 
 9     @property
10     def height(self):
11         return self._height
12     @height.setter
13     def height(self,value):
14          self._height=value
15 
16     @property
17     def resolution(self):  #此处将resolution定义为只读属性
18         return self.width*self.height
19     
20 s = Screen()
21 s.width = 1024
22 s.height = 768
23 print('resolution =', s.resolution)
24 if s.resolution == 786432:
25     print('测试通过!')
26 else:
27     print('测试失败!')

运行结果:

多重继承

  参考:https://kevinguo.me/2018/01/19/python-topological-sorting/

    多重:多个, 多重继承:即同时继承多个对象

     通过多重继承,一个子类就可以同时获得多个父类的所有功能

     MixIn

    在设计类的继承关系时,通常,主线都是单一继承下来的,但是,如果需要“混入”额外的功能,通过多重继承就可以实现,这种设计通常称之为MixIn.

    python允许使用多重继承,因此,MixIn就是一种常见的设计

    只允许单一继承的语言(如JAVA),不能使用MixIn设计

    目的:MixIn主要解决对同一对象用不同标准来分类的问题。从性别划分,你是男人;从国家划分,你是中国人;从职业划分,你是程序员; mixin可以轻松定义具备跟你一样特征的人:中国男程序员(天朝屌丝逗比程序员)

定制类

    __str__

    将__str__方法理解成,规范化输出格式,输出用户能看懂的格式

   __repr__

   

     直接显示变量调用的不是__str__( ),而是__repr__( ),两者的区别:

          __str__( )返回用户看到的字符串

         __repr__( )返回程序开发者看到的字符串,__repr__( )是为调试服务的

    通常情况下,__repr__( )和 __str__( )的代码相同,所以偷懒时可以写成这种形式:__repr__=__str__ ,类似赋值,把__str__ 里的代码赋给__repr__

1 class Student(object):
2     def __init__(self,name):
3         self.name=name
4 
5 s = Student("Michael")
6 print(s)

运行结果:

   下面使用了__str__方法后:

1 class Student(object):
2     def __init__(self,name):
3         self.name=name
4     def __str__(self):
5         return 'Student Object (name :%s) ' % self.name  #返回的内容,自己可以修改
6 
7 s=Student("Michael")
8 print(s)

 返回用户能够看懂的格式:

  __str__方法 return 后面的语句可以自己修改

1 class Student(object):
2     def __init__(self,name):
3         self.name=name
4     def __str__(self):
5         return "Hello, My name is %s" % self.name
6 
7 s=Student("Michael")
8 print(s)

 

    __iter__

      如果一个类想被用于for ……in 循环,类似list、tuple那样,就必须使用一个__iter__( )方法, 该方法返回一个迭代对象

举例:以斐波那契数列为例,写一个Fib类,可以作用于for循环:

 1 class fib(object):
 2     def __init__(self):
 3         self.a,self.b=0,1
 4 
 5     def __iter__(self):
 6         return self  #实例本身就是迭代对象,所以返回自己
 7 
 8     def __next__(self):
 9         self.a,self.b=self.b,self.a+self.b
10         if self.a>500:
11             raise StopIteration()
12         return self.a
13 
14 for n in fib():
15     print(n)

 __getitem__

      取下标

    __getitem__( self, item)方法

   item看作是“下标”, __getitem__( )传入的参数可能是一个int,也可能是一个切片对象slice

     与 __getitem__对应的是__setitem__( )方法,__delitem__( )方法

 1 class fib(object):
 2     def __init__(self):
 3         self.a,self.b=0,1
 4 
 5     def __iter__(self):  #__iter__方法,返回一个迭代对象
 6         return self
 7 
 8     def __next__(self):
 9         self.a,self.b=self.b,self.a+self.b
10         if self.a>100:
11             raise StopIteration
12         return self.a
13 
14     def __getitem__(self,n):  #__getitem__方法
15         a,b=1,1
16         for x in range(n):
17             a,b = b, a+b
18         return a
19 
20 f=fib()
21 print('f[0]:',f[0])
22 print("f[1]:",f[1])
23 print("f[2]:",f[2])
24 print("f[3]:",f[3])
25 print("f[13]:",f[13])

运行结果:

__getattr__

     正常情况下,当调用的类的方法或属性不存在时,就会报错。

1 class Student(object):
2     def __init__(self):
3         self.name="lily"
4 
5 s=Student()
6 print("s.name:",s.name)
7 print("    ")
8 print("s.score:",s.score)
9 print("s.score函数:",s.score())

    由于类中没有score属性时,调用会报错

 print("s.score:",s.score)语句的报错结果:

 print("s.score函数:",s.score)语句的报错结果:

 

 添加__getattr__方法后: return 99,所以调用时s.score即可

 1 class Student(object):
 2     def __init__(self):
 3         self.name="lily"
 4 
 5     def __getattr__(self, attr): #__getattr__方法
 6         if attr == 'score':
 7             return 99
 8            
 9 
10 
11 s=Student()
12 print("s.name:",s.name)
13 print("    ")
14 print("s.score:",s.score)

返回函数也是可以的: return lambda : 25,所以调用时是s.score()

 1 class Student(object):
 2     def __init__(self):
 3         self.name="lily"
 4 
 5     def __getattr__(self, attr): #__getattr__方法
 6         if attr == 'score':
 7             return lambda :25  #此处是匿名函数lambda, 之前只是返回一个数值, return 99
 8             # print('hell')
 9 
10 
11 s=Student()
12 print("s.name:",s.name)
13 print("    ")
14 print("s.score:",s.score())#由于return后面返回的是一个函数,因此这里调用时应该是s.score(),带上括号,
15 # 而如果是return 99, 则调用时不必带括号

运行结果和上面相同:

__call__

    调用实例方法时,使用“instance.method( )”形式来调用

   问题:能不能直接在实例本身上调用呢?在python中,是肯定的

    结果:任何类,只需要定义一个__call( )方法,就可以直接对实例进行调用

   

1 class Student(object):
2     def __init__(self,name):
3         self.name=name
4 
5     def __call__(self):
6         return 'my name is %s' %self.name
7 
8 s=Student('lily')
9 print(s())

猜你喜欢

转载自www.cnblogs.com/bravesunforever/p/10462557.html