【python基础】 面向对象高级编程

本章包含:_slots_、@property、多重继承、定制类、枚举类、元类
参考:廖雪峰python

面向对象高级编程

  • __slots__
    • 动态方法
    • 限制可改变变量
  • @property
    • 修改和得到变量
  • 多重继承
    • 定义
  • 定制类
    • str和repr
    • iter和next
    • getitem
    • getattr
    • call
  • 枚举类
    • 定义
  • 元类

slots

动态方法

实例可以动态添加方法,使用types库的 Methodtype

class Student():
    pass

s = Student()
s1 = Student()
s.name = 10
print(s.name)


from types import MethodType
def haha(self):
    print('haha')
s.haha = MethodType(haha,s)
s.haha()
s1.haha() #会报错 AttributeError

但是实例动态添加方法只是针对独立的实例,可为类动态添加方法

class Student():
    pass
def haha(self):
    print('haha')
    
from types import MethodType
Student.haha = MethodType(haha,Student)

s1 = Student()
s2 = Student()
s1.haha()
s2.haha()

限制可改变变量

使用__slots__(xx)可限制 可变变量。但只对该类有用,子类的slot包括了子类本身slot和父类的slot。

class Student(object):
    __slots__ = ('name', 'age')
    pass

s = Student()
s.name = 10
s.age = 10
#s.haha = 10  # 会报错 AttributeError

@property

修改和得到变量

@property和 @xxx.setter 既能检查参数,又可以用类似属性这样简单的方式来访问类的变量。设置了@property就可以设置@xxx.setter

class Student():
    @property
    def birth(self):
        return self._birth

    @birth.setter
    def birth(self, value):
        self._birth = value

    @property
    def age(self): #只可读,修改会报错
        return 2022 - self._birth
    
s = Student()
s.birth=2000
print(s.birth)    
print(s.age)

#2000
#22

多重继承

定义

定义如下
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass

MixIn的目的就是给一个类增加多个功能,这样,在设计类的时候,我们优先考虑通过多重继承来组合多个MixIn的功能,而不是设计多层次的复杂的继承关系。可命名为Mixln

定制类

str和repr

直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

class Student():
    def __init__(self,name):
        self.name = name
    def __str__(self):
        return "Student's name is {}".format(self.name)
    def __repr__(self):
        return "Student's name is {} (repr version)".format(self.name)

s = Student('Jeff')
print(s)
s

#Student's name is Jeff
#Student's name is Jeff (repr version)

iter和next

如果一个类想被用于for … in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象(自身),然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

class Mieee():
    def __init__(self):
        self.mie = 0
    def __iter__(self):
        return self
    def __next__(self):
        self.mie +=1
        if self.mie>20:
            raise StopIteration()
        return self.mie
    
m = Mieee()
for i in m:
    print(i,end=' ')

#1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

getitem

实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:
【python基础】 面向对象高级编程_第1张图片
使用__getitem__

def __getitem__(self, n):
        if isinstance(n, int): # n是索引
            a, b = 1, 1
            for x in range(n):
                a, b = b, a + b
            return a
        if isinstance(n, slice): # n是切片
            start = n.start
            stop = n.stop
            if start is None:
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)
                a, b = b, a + b
            return L

m = Mieee()
print(m[5])
print(m[5:10])

#8
#[8, 13, 21, 34, 55]

getattr

当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值.

只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找.

注意,任意调用其他不在getattr的属性都会返回None,因为getattr默认返回None,如果想只对特定几个有相应,要跑出AttributeError

class Mie():
    def __init__(self):
        self.name = 'Jeff'
    def __getattr__(self,attr):
        if attr == 'score':
            return 99 #属性
        if attr == 'age':
            return lambda:88 #方法
         raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)
        
m = Mie()
print(m.score)
print(m.age())

#99
#88

【python基础】 面向对象高级编程_第2张图片

call

__call__()调用实例本身
还可以定义参数。对实例进行直接调用就好比对一个函数进行调用一样,所以你完全可以把对象看成函数,把函数看成对象,因为这两者之间本来就没啥根本的区别。

class Mie():
    def __call__(self):
        print('You are diaoyonging yourself')

m = Mie()
m()

#You are diaoyonging yourself

枚举类

定义

当我们需要定义常量时,更好的方法是为这样的枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例。Python提供了Enum类来实现这个功能

from enum import Enum

Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))

for name,member in Month.__members__.items():
    print(name,'=>',member,',',member.value)

Jan => Month.Jan , 1
Feb => Month.Feb , 2
Mar => Month.Mar , 3
Apr => Month.Apr , 4
May => Month.May , 5
Jun => Month.Jun , 6
Jul => Month.Jul , 7
Aug => Month.Aug , 8
Sep => Month.Sep , 9
Oct => Month.Oct , 10
Nov => Month.Nov , 11
Dec => Month.Dec , 12

也可以继承派生出自定义类,unique装饰器判断是否有重复

from enum import Enum, unique

@unique
class Age(Enum):
    Jeff = 22
    Jack = 21
    Jason = 20
    Rain = 19
    Joa = 18
    
J1 = Age.Jeff
print(J1)
print(J1.value)

#Age.Jeff
#22

元类

当在一个文件中创建类,另一个文件中调用时,如下

# hello.py
class Hello(object):
    def hello(self, name='world'):
        print('Hello, %s.' % name)

# main.py
 from hello import Hello
 h = Hello()
 h.hello()
Hello, world.
print(type(Hello))
#
print(type(h))
#

可见Hello类型为type,实例类型为hello.Hello,可以通过函数type()创建这样一个类

def fun(self,name='Jeff'):
    print('hello {0} '.format(name))

Hello = type('Hello',(object,),dict(hello=fun))#创建Hello class

h = Hello()
h.hello()
print(type(Hello))
print(type(h))

#hello Jeff 
#
#

type第一个参数为类名,第二个参数为父类名(单元素写法有逗号),第三个参数为方法名字和函数绑定。

你可能感兴趣的:(python基础,python,开发语言)