Python3入门笔记七之面向对象高级编程

简介: '限制实例的属性:__slots__ ,把一个方法变成属性调用:@property装饰器,多重继承&MixIn,定制类,枚举类:Enum,元类'限制实例的属性:__slots__限制实例的属性 比如只允许对Student实例添加name和age属性。 由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。注意:__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的


'限制实例的属性:__slots__ ,把一个方法变成属性调用:@property装饰器,多重继承&MixIn,定制类,枚举类:Enum,元类'


限制实例的属性:__slots__


限制实例的属性 比如只允许对Student实例添加name和age属性。 由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。注意:__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的


class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student()
s.name='a'
s.age=11
s.score=99 # AttributeError: 'Student' object has no attribute 'score'
复制代码


子类实例允许定义的属性就是自身的__slots__加上父类的__slots__


把一个方法变成属性调用:@property装饰器


把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值


class Student(object):
    @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
s = Student()
s.score=11
s.score
复制代码


不写 @score.setter 的话就只能读这个属性


多重继承&MixIn


(⊙﹏⊙) 这个就和java的不同了,在java中只能单继承,多实现。 而在这个python中,直接在括号中添加对应的父类即可。


class Dog(Mammal, RunnableMixIn):
    pass
复制代码


上面这个例子在设计上也称为mixin


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


一般单继承的class名称不加后缀MixIn(如Mammal),这个多继承的点要加后缀MixIn如(RunnableMixIn),


Python自带了TCPServerUDPServer这两类网络服务,而要同时服务多个用户就必须使用多进程多线程模型,这两种模型由ForkingMixInThreadingMixIn提供。

多线程模式的UDP服务:


class MyUDPServer(UDPServer, ThreadingMixIn):
    pass
复制代码


定制类


python3官方文档定制类介绍


__str__(),__repr__()


这两个同Java的toString()方法,只不过这个__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,__repr__()是为调试服务的


class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self): # 2
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__ # 1
s=Student('ken')
s # 2
print(s) # 1 Student object (name=ken)
# 同时注释掉 # 1 的内容就可以看到效果
复制代码


__iter__


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


这个博主之前的文章中也也有记录到:《python3入门笔记三之高级特性---切片,迭代,列表生成式,生成器,迭代器


网络异常,图片无法展示
|



class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化两个计数器a,b
    def __iter__(self):
        return self # 实例本身就是迭代对象,故返回自己
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 计算下一个值
        if self.a > 100: # 退出循环的条件
            raise StopIteration()
        return self.a # 返回下一个值
for n in Fib():
    print(n) # 1 1 2 3 5 8...89
复制代码


__getitem__


  1. 按照下标取出元素


class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a
f = Fib()
f[10] # 89
复制代码


  1. 切片


getitem()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:


class Fib(object):
    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
            step = n.step
            print('step:',step)
            if start is None:
                start = 0
            if step is None:
                step = 1
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    if(x%step==0):
                        L.append(a)
                a, b = b, a + b
            return L
f=Fib()
print(f[:10]) #  [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 
print(f[:10:2]) # [1, 2, 5, 13, 34]
print(f[10]) # 89
复制代码


__call__


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

怎么判断一个变量是对象还是函数呢? 通过callable()函数,我们就可以判断一个对象是否是“可调用”对象。


class Student(object):
    def __init__(self, name):
        self.name = name
    def __call__(self):
        print('My name is %s.' % self.name)
s = Student('Michael')
if callable(s):
    s() # My name is Michael.
复制代码


动态返回一个属性:__getattr__


这个在上篇笔记中有写到《python入门笔记六之面向对象编程

网络异常,图片无法展示
|


class Student(object):
    def __getattr__(self, attr):
        if attr=='age':
            return lambda: 25
        elif attr =='name':
            return 'Amy'
s = Student()
print(s.age()) # 25 注意这里返回的是一个lambda表达式,需要调用该方法才能拿到值
print(s.name) # Amy 
复制代码


链式调用的例子 :


class Chain(object):
    def __init__(self, path=''):
        self._path = path
    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))
    def __call__(self, path):
        return Chain('%s/%s' % (self._path, path))
    def __str__(self):
        return self._path
    __repr__ = __str__
print(Chain().status.user.timeline.list) # /status/user/timeline/list
# 注意这里的chain返回的是chain对象,所以这个users('michael')就可以很好地理解了。
print(Chain().users('michael').repos) # /users/michael/repos
复制代码


使用枚举类:Enum


枚举类型定义一个class类型,然后,每个常量都是class的一个唯一实例


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)
# value属性则是自动赋给成员的int常量,默认从1开始计数。
# Jan => Month.Jan , 1
# Feb => Month.Feb , 2
# Mar => Month.Mar , 3
# .
# .
# .
# Dec => Month.Dec , 12
复制代码


获取枚举常量的两种方式:


  1. 根据成员名称


  1. 根据value值


from enum import Enum, unique
@unique # @unique装饰器可以帮助我们检查保证没有重复值。
class Weekday(Enum):
    Sun = 0 # Sun的value被设定为0
    Mon = 1
    Tue = 2
    Wed = 3
    Thu = 4
    Fri = 5
    Sat = 6
print(Weekday.Mon,'=',Weekday.Mon.value) # Weekday.Mon = 1
print(Weekday['Mon'],'=',Weekday.Mon.value)  # Weekday.Mon = 1
print(Weekday(1),'=',Weekday.Mon.value)  # Weekday.Mon = 1
for name, member in Weekday.__members__.items():
    print(name, '=>', member)
复制代码


小测试: 把gender属性改造为枚举类型,避免使用字符串


class Gender(Enum):
    Male = 0
    Female = 1
class Student(object):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
    def __str__(self):
          return 'Student object name={},gender={}'.format(self.name,self.gender)
    __repr__=__str__
bart = Student('Bart', Gender.Male)
print(bart) #  Student object name=Bart,gender=Gender.Male
bart # Student object name=Bart,gender=Gender.Male
复制代码


使用元类


type()


这个也在上篇博文讲到,现在要补充下它的其他知识点


  1. type()函数可以查看一个类型或变量的类型


  1. type()函数既可以返回一个对象的类型,又可以创建出新的类型


网络异常,图片无法展示
|


print(type(str)) # <class 'type'>  str是class,所以类型是type
print(type('123')) # <class 'str'> '123'是实例。所以类型是str
def fn(self, name='world'): # 先定义函数    
    print('Hello, %s.' % name)
复制代码


通过type()函数创建出Hello类,而无需通过class Hello(object)...的定义:


要创建一个class对象,type()函数依次传入3个参数:


  1. class的名称;


  1. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法


  1. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。


如图(通过这些ide可以帮助我们更快认识这个函数):


网络异常,图片无法展示
|


def fn(self, name='world'):
    return name
Hello=type('Hello',(object,),dict(hello=fn)) # 创建出Hello类
h=Hello() # 注意这里才去创建Instance实例
print(h.hello('1'))
print(type(Hello)) # <class 'type'>
print(type(h)) # <class '__main__.Hello'>
复制代码


metaclass


除了使用type()动态创建类以外,要控制类的创建行为,还可以使用 metaclassmetaclass是Python面向对象里最难理解,也是最难使用的魔术代码 (已劝退😆 哈哈哈)


大概理解就是它可以定义创建类时候的行为。


# metaclass是类的模板,所以必须从`type`类型派生:
class ListMetaclass(type):
    def __new__(cls, name, bases, attrs):
        attrs['add'] = lambda self, value: self.append(value)
        return type.__new__(cls, name, bases, attrs)
class MyList(list, metaclass=ListMetaclass):
    pass
L = MyList()
L.add(1)
L # 1
复制代码


一般写代码的时候用不上,使用metaclass的典型例子是ORM框架(😱老底都揭露出来了)另外再写写这个例子😄



目录
相关文章
|
1天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
12 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
2天前
|
存储 人工智能 数据挖掘
从零起步,揭秘Python编程如何带你从新手村迈向高手殿堂
【10月更文挑战第32天】Python,诞生于1991年的高级编程语言,以其简洁明了的语法成为众多程序员的入门首选。从基础的变量类型、控制流到列表、字典等数据结构,再到函数定义与调用及面向对象编程,Python提供了丰富的功能和强大的库支持,适用于Web开发、数据分析、人工智能等多个领域。学习Python不仅是掌握一门语言,更是加入一个充满活力的技术社区,开启探索未知世界的旅程。
12 5
|
2天前
|
Python
探索Python装饰器:从入门到实践
【10月更文挑战第32天】在编程世界中,装饰器是一种特殊的函数,它允许我们在不改变原有函数代码的情况下,增加额外的功能。本文将通过简单易懂的语言和实际案例,带你了解Python中装饰器的基础知识、应用以及如何自定义装饰器,让你的代码更加灵活和强大。
9 2
|
2天前
|
人工智能 数据挖掘 开发者
探索Python编程:从基础到进阶
【10月更文挑战第32天】本文旨在通过浅显易懂的语言,带领读者从零开始学习Python编程。我们将一起探索Python的基础语法,了解如何编写简单的程序,并逐步深入到更复杂的编程概念。文章将通过实际的代码示例,帮助读者加深理解,并在结尾处提供练习题以巩固所学知识。无论你是编程新手还是希望提升编程技能的开发者,这篇文章都将为你的学习之旅提供宝贵的指导和启发。
|
1天前
|
SQL 数据挖掘 Python
数据分析编程:SQL,Python or SPL?
数据分析编程用什么,SQL、python or SPL?话不多说,直接上代码,对比明显,明眼人一看就明了:本案例涵盖五个数据分析任务:1) 计算用户会话次数;2) 球员连续得分分析;3) 连续三天活跃用户数统计;4) 新用户次日留存率计算;5) 股价涨跌幅分析。每个任务基于相应数据表进行处理和计算。
|
1天前
|
机器学习/深度学习 人工智能 数据可视化
探索Python编程:从基础到高级
【10月更文挑战第33天】本文是一篇深入浅出的Python编程入门教程,适合初学者阅读。文章首先介绍了Python的基本概念和语法,然后通过实例讲解了如何使用Python进行数据处理和分析,最后介绍了一些高级特性和库,帮助读者更好地掌握Python编程。无论你是编程新手还是有一定经验的开发者,这篇文章都能给你带来新的启示和收获。
|
2天前
|
存储 人工智能 数据挖掘
探索Python编程的奥秘
【10月更文挑战第32天】在这篇文章中,我们将一起踏上一段奇妙的Python编程之旅。从基础语法到高级特性,我们将通过一系列简单而直观的代码示例,逐步揭开Python语言背后的神秘面纱。无论你是初学者还是有一定经验的开发者,这篇文章都将为你提供新的视角和深入的理解。让我们一起开始这段旅程吧!
|
2天前
|
存储 机器学习/深度学习 搜索推荐
Python编程入门:从零开始构建你的第一个程序
【10月更文挑战第32天】本文旨在通过浅显易懂的方式引导编程新手进入Python的世界。我们将一起探索Python的基础语法,并通过实例学习如何构建一个简单的程序。文章将不直接展示代码,而是鼓励读者在阅读过程中自行尝试编写,以加深理解和记忆。无论你是编程初学者还是希望巩固基础知识的开发者,这篇文章都将是你的良师益友。让我们开始吧!
|
6月前
|
人工智能 Java Python
python入门(二)安装第三方包
python入门(二)安装第三方包
|
29天前
|
机器学习/深度学习 Python
【10月更文挑战第5天】「Mac上学Python 6」入门篇6 - 安装与使用Anaconda
本篇将详细介绍如何在Mac系统上安装和配置Anaconda,如何创建虚拟环境,并学习如何使用 `pip` 和 `conda` 管理Python包,直到成功运行第一个Python程序。通过本篇,您将学会如何高效地使用Anaconda创建和管理虚拟环境,并使用Python开发。
56 4
【10月更文挑战第5天】「Mac上学Python 6」入门篇6 - 安装与使用Anaconda