python 指南(08)面向对象高级

本文深入探讨Python的面向对象编程,包括使用`__slots__`限制实例属性,`@property`装饰器,多继承,枚举类以及元类的应用。讲解了如何定制类的行为,如重载`__str__`,`__iter__`,`__getitem__`,`__getattr__`方法,以及使用元类创建动态类和简单的ORM框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用__slots__

一、实例和类动态绑定方法

1、实例动态绑定方法

  • 绑定的方法只有这一个实例可以使用,该类的其他实例不能用
from types import MethodType


class Student(object):
    pass


def set_age(self, age):  # 定义一个函数作为实例方法
    self.age = age


s = Student()
s.set_age = MethodType(set_age, s)  # 给实例绑定一个方法
s.set_age(25)  # 调用实例方法
print(s.age)  # 测试结果

2、类动态绑定方法

def set_score(self, score):
     self.score = score

Student.set_score = set_score

二、使用__slots__限制实例属性绑定

  • 就是定义一个tuple类型的类属性来限制实例属性的动态添加,类还是可以不受限制动态绑定属性的
  • 下面的代码表示除了name和age不能加其他的属性了这个类的实例
  • 注意一点子类不受父类slots的影响,除非子类中也定义了 slots,这样子类实例允许定义的属性就是自身的slots加上父类的slots
class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称

使用@property

  • @property广泛应用在类的定义中,用来定义属性可以让调用者写出简短的代码,同时保证对参数进行必要的检查,这样,程序运行时就减少了出错的可能性
  • 下面就是property的使用方法,age没有定义setter,是一个只读属性;使用的时候取值和赋值都是直接进行,不必通过get/set方法;property本质就是一个装饰器
class Student(object):

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

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

    @property
    def age(self):
        return 2015 - self._birth

	@property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not isinstance(value, int):
            raise ValueError('score must be an integer!')
        if value < 0 or value > 100:
            raise ValueError('score must between 0 ~ 100!')
        self._score = value

多重继承

  • 单一的继承结构导致如果程序比较庞大,我们需要设计的类就会很多,继承的结构会十分复杂
  • python中可以实现多继承,可以大大减少类的数量和继承结构的复杂程度
  • python中多继承的方式被称为 MixIn,具体来说就是:在设计类的继承关系时,主线都是单一继承下来的,如果需要 混入 额外的功能,通过多重继承就可以实现
  • MixIn 的目的就是给一个类增加多个功能,这样一来我们不需要复杂而庞大的继承链,只要选择组合不同的类的功能,就可以快速构造出所需的子类
class Dog(Mammal, Runnable):
    pass

定制类

  • python 类中的特殊变量可以帮助我们用来定制类
  • 比如前面的__slots__可以限制实例属性添加,__len__可以作用于len()函数
class Demo(object):
    def __len__(self):
        return 100


d = Demo()
print(len(d))

一、__str__

  • 就是类似于java中的toString方法,打印对象相关信息的
  • 需要注意的是这个方法只会在print方法中被调用,交互模式直接输入对象变量回车显示对象信息内容和__repr__有关,保证二者一样的定义就可以保证无论是print还是直接变量输出都是我们想看到的对象信息的输出
class Student(object):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Student object:(%s)' % self.name

    #__repr__ = __str__


s = Student("allen")
print(s)

在这里插入图片描述

二、__iter__

  • 如果一个类想被用于for … in循环,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环
class Fib(object):
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        while self.a > 10000:
            raise StopIteration()
        return self.a


fib = Fib()
for i in range(10):
    print(next(fib), end="  ")

for f in fib:
    print(f)

三、__getitem__

  • 适当实现__getitem__可以让自定义的类实现类似list的索引取值和切片等操作
  • 相关的还有__setitem____delitem__,这些所有的方法如果有恰当的实现我们自定义的类完全就可以实现list、dict等容器的行为功能,这也是动态语言的鸭子类型的体现
class Fib(object):
    def __getitem__(self, item):
        if isinstance(item, int):
            a, b = 0, 1
            for x in range(item):
                a, b = b, a + b

            return b
        if isinstance(item, slice):
            stop = item.stop
            start = item.start
            step = item.step
            if start is None:
                start = 0
            if step is None:
                step = 1
            a, b = 1, 1
            L = []
            for i in range(stop):
                if i >= start and (i - start) % step == 0:
                    L.append(a)
                a, b = b, a + b
            return L


fib = Fib()
print(fib[5])
print(fib[:5])
print(fib[0:8])
print(fib[0:8:2])
print(fib[0:8:3])

# ouput:
# 8
# [1, 1, 2, 3, 5]
# [1, 1, 2, 3, 5, 8, 13, 21]
# [1, 2, 5, 13]
# [1, 3, 13]

四、__getattr__

  • 这个的作用就是当我们试图获取一个对像的属性的时候,python解析器首先会搜索这个对象里面是否有这个属性,有的话就直接返回,没有的话就会试图调用__getattr来返回属性,默认返回None,再没有的话就会报错
  • 这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段
class Chain(object):

    def __init__(self, path=''):
        self._path = path

    def __getattr__(self, path):
        print(path,end=" : ")
        print(self._path)
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__


print(Chain().status.user.timeline.list)

在这里插入图片描述

五、__call__

  • 任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用
  • 对象和函数之间本来就没有根本上的区别,使用了可调用的对象概念后,二者之间的界限更加模糊了
  • 将可调用对象看成函数,那么我们就可以在运行期间动态创建函数
  • 通过callable()函数可以判断一个对象是否是可调用对象
class Haha():
    def __call__(self, *args, **kwargs):
        print('call-test')


Haha()()
print(callable(Haha()))

在这里插入图片描述


使用枚举类

  • 定义一个class类型,然后每个常量都是class的一个唯一实例,就是所谓的枚举类型
  • 枚举的本质就是一组离散的整数,默认是从1开始的,但是也可以自己指定

一、python提供的枚举支持

  • 枚举的基本使用
from enum import Enum

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

print(Month.Mar)
print(Month.Mar.value)
  • 枚举类型的遍历
for name, member in Month.__members__.items():
    print(name, '=>', member, ',', member.value)

二、自定义枚举类

  • @unique装饰器可以确保枚举没有重复值
from enum import Enum, unique

@unique
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6

使用元类

一、type()方法

  • type()函数既可以返回一个对象的类型,又可以创建出新的类型
  • 我们在py文件中进行class的定义,本质上最后还是由编译器通过type方法为我们创建类
  • 动态语言的灵活性体现之一就在于可以动态创建类,这对于静态语言是比较难实现的
  • type创建类第一个参数是类名,第二个是父类元组,第三个是类方法和属性的定义
def fn(self, text='world'):
    print('hello %s' % text)


def f1(self, age):
    self.age = age


Hello = type("Hello", (object,), {'hello': fn, 'name': 'mike', '__init__': f1})

h = Hello(12)
h.hello('allen')
h.hello()
print(h)
print(Hello)
print(type(h))
print(type(Hello))
print(h.name)
print(Hello.name)
print(h.age)

在这里插入图片描述

二、metaclass(元类)

  • 连接起来就是:先定义metaclass,就可以创建类,最后创建实例
  • metaclass允许你创建类或者修改类,你可以把类看成是metaclass创建出来的实例

1、元类的简单使用

  • 通过自定义的元类,这里就给自定义的list加上了一个add方法
class listmetaClass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda lis, ite: lis.append(ite)
        return type.__new__(cls, name, bases, attrs)


class MyList(list, metaclass=listmetaClass):
    pass


l = MyList()
l.add('1')
l.add('54')
l.add('65')
print(l)

1、用元类写一个简单orm框架


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值