Python之旅(八)面向对象02

isinstance(obj,cls)和issubclass(sub,super)

isinstance(obj,cls)检查是否obj是否是类 cls 的对象
 issubclass(sub, super)检查sub类是否是 super 类的派生类(子类)

class Animal():
    pass

class Dog(Animal):
    pass

d1 = Dog()
# 检查d1是不是Dog的一个对象
print(isinstance(d1, Dog))

print(issubclass(Dog, Animal))
print(issubclass(Animal, object))

# 运行结果
# True
# True
# True

反射

概念:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)

四个可以实现自省的函数:

这四个方法适用于对象和类(一切皆对象,类也是一种对象)

 1 class Dog():
 2     type = ‘犬科‘
 3
 4     def __init__(self, name, gender):
 5         self.name = name
 6         self.gender =gender
 7
 8     def info(self):
 9         print(‘name is {}, gender is {}‘.format(name, gender))
10
11 d1 = Dog(‘小黄‘, ‘母‘)
12
13 # 检测是否可以使用某属性(数据属性、方法属性)
14 print(hasattr(d1, ‘type‘))
15
16 # 获取属性对应的属性值
17 print(getattr(d1, ‘type‘))
18 #print(getattr(d1, ‘age‘)) #找不到 d1.age 报错
19 print(getattr(d1, ‘age‘, ‘没有找到age‘)) #可以设置返回值,找不不报错
20
21 # 设置属性
22 setattr(d1, ‘type‘, ‘大黄‘)
23 print(getattr(d1, ‘type‘))
24 setattr(d1, ‘add‘, lambda x:x+1) #也可以设置函数属性
25 print(d1.add) #<function <lambda> at 0x02EE07C8>
26 print(d1.add(3)) #4
27
28
29 # 删除属性
30 delattr(d1, ‘type‘)
31 #delattr(d1, ‘abc‘) #不存在,报错
32
33 print(d1.__dict__)

动态导入模块

 1 # from mod import a
 2
 3 # import mod.a
 4
 5 # 使用__import__导入,拿到的是最顶级的模块
 6 m = __import__(‘mod.a‘)
 7 print(m) #<module ‘mod‘ (namespace)>
 8 m.a.p()
 9
10 # 使用importlib模块处理模块的导入操作时,拿到的时目标模块
11 import  importlib
12 m = importlib.import_module(‘mod.a‘)
13 print(m) #<module ‘mod.a‘ from ‘E:\\PycharmProjects\\untitled\\day26\\mod\\a.py‘>
14 m.p()

__getattr__ __setattr__ __delattr__

# __getattr__ __setattr__ __delattr__是类的内置函数,dir()查看
class Dog():
    type = ‘animal‘
    def __init__(self, name):
        self.name = name

    # 实例使用点.调用不存在的属性时触发
    def __getattr__(self, item):
        print("from getattr 没有找到该属性")

    # 添加修改实例属性时触发
    def __setattr__(self, key, value):
        print(‘from setattr ‘)
        #self.key = value #这就无限递归了!
        #self.__dict__[key] = value #正确赋值方法

    # 删除实例属性时触发
    def __delattr__(self, item):
        print("from delattr")

d1 = Dog(‘小黄‘)
print(d1.__dict__)
#因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值

d1.type #可以访问到,没有触发__getattr__
d1.namessssss

d1.name = "大黄"
d1.__dict__[‘age‘] = 18 #我们可以直接修改属性字典,来完成添加/修改属性的操作
print(d1.__dict__)

del d1.age

执行结果:

1 from setattr
2 {}
3 from getattr 没有找到该属性
4 from setattr
5 {‘age‘: 18}
6 from delattr

包装和授权

包装:python为我们提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就是包装。也就是对基本数据类型进行二次加工

通过继承和派生实现包装

class List(list):
    def append(self, object):
        ‘派生自己的appdend:只允许追加int类型‘
        if type(object) is int:
            super().append(object)
        else:
            print(‘只支持append整型int‘)

    @property
    def middle(self):
        """新增一个方法,用于获取列表的中间值"""
        i = int(len(self)/2)
        return self[i]

x = [1, 2, 3, 4]
print(type(x))

lx = List(x)
print(type(lx))

lx.append(5)
print(lx)
lx.append(‘a‘)

print(lx.middle)

# 运行结果
# <class ‘list‘>
# <class ‘__main__.List‘>
# [1, 2, 3, 4, 5]
# 只支持append整型int
# 3

通过授权实现包装

授权:通过授权的方式实现包装

授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

 1 class List():
 2     def __init__(self, seq):
 3         self.seq = seq
 4
 5     def append(self, obj):
 6         if type(obj) is int:
 7             self.l.append(obj)
 8         else:
 9             print("只能append整型int")
10
11     def __getattr__(self, item):
12         return getattr(self.seq, item)
13
14     def __str__(self):
15         return str(self.seq)
16
17 i = List([1, 2])
18 i.append("a")
19 print(i)

__getattribute__

 1 class Dog():
 2     def __init__(self, name):
 3         self.name = name
 4
 5     def __getattr__(self, item): #当getattrbute抛出AttributeError异常时才执行
 6         print("这个玩意不存在from getattr")
 7
 8     def __getattribute__(self, item): #实例寻找变量时都会触发
 9         print(‘无论是否存在都执行 from getattrbute‘)
10         raise AttributeError(‘这个玩意不存在,我要抛出一个异常‘)
11
12 d1 = Dog(‘小黄‘) #触发__getattribute__,但是这里没有成功赋值
13 d1.name
14 d1.age

__setitem__,__getitem,__delitem__

在使用实例字典进行操作时会触发 __setitem__   __getitem__  __delitem__

在使用实例加点对变量进行操作时会触发 __setattr__  __getattr__  __delattr__

 1 class Dog():
 2     def __getitem__(self, item):
 3         print(‘from getitem‘)
 4         # return self.__dict__[item] #返回操作
 5
 6     def __setitem__(self, key, value):
 7         print(‘from setitem‘)
 8         #self.__dict__[key] = value 赋值操作
 9     def __delitem__(self, key):
10         print(‘from delitem‘)
11         #self.__dict__.pop(key) #删除操作
12
13 d1= Dog()
14 d1.age = 11 #使用实例名加点 . 的方式会触发getattr,不会触发getitem
15 print(d1.__dict__)
16
17 del d1[‘age‘] #使用字典的方式,会触发delitem
18 d1[‘name‘] = ‘小黄‘
19 print(d1[‘name‘])

_str__,__repr__,__format__

常见的对象显示字符串的格式:

l = [1, 2, 3]
print(l)

class Dog():
    pass

d1 = Dog()
print(d1)

f = open(‘test.txt‘, ‘w‘)
print(f)

# 运行结果如下
# [1, 2, 3]
# <__main__.Dog object at 0x00CA9C30>
# <_io.TextIOWrapper name=‘test.txt‘ mode=‘w‘ encoding=‘cp936‘>

_str__,__repr__,__format__必须有返回值,且返回值只能是字符串,否则抛异常

  print()和str()都会触发obj.__str__()

  repr()或者交互解释器中都会触发obj.__repr__()

  如果str没有定义,则使用repr来代替输出

str和repr

class Dog():
    def __init__(self, name):
        self.name = name

    def __str__(self):
        print(‘from __str__‘)
        return (‘这条狗的名字是%s‘ %self.name)

    def __repr__(self):
        return (‘from repr: %s‘ %self.name)

d1 = Dog(‘小黄‘)
print(d1) #print()和str()都会触发obj.__str__()
str(d1)

print(repr(d1)) #repr()或者交互解释器中都会触发obj.__repr__()

# str repr的必须有返回值,返回值必须是字符串,否则跑异常
# 如果str没有定义,则使用repr来代替输出

# 运行结果
# from __str__
# 这条狗的名字是小黄
# from __str__
# from repr: 小黄

format

class Dog():
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender =gender
    def __format__(self, format_spec):
        return ‘from format‘

d1 = Dog(‘小黄‘, 18, ‘公‘)
print(format(d1)) #format()触发__format__()
print(‘{}‘.format(d1)) #触发__format()

# 运行结果
# from format
# from format
format_dict = {
    ‘nag‘: ‘name:{0.name}, age:{0.age}, gender:{0.gender}‘,
    ‘agn‘: ‘age:{0.age}, gender:{0.gender}, name:{0.name}‘,
    ‘gna‘: ‘gender:{0.gender}, name:{0.name}, age:{0.age}‘,
}

class Dog():
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender =gender
    def __format__(self, format_spec):
        if not format_spec or format_spec not in format_dict:
            format_spec = ‘nag‘
        i = format_dict[format_spec]
        return i.format(self)

d1 = Dog(‘小黄‘, 18, ‘公‘)

print(format(d1, ‘agn‘))
print(format(d1, ‘dasjgi sd h‘))

# 运行结果
# age:18, gender:公, name:小黄
# name:小黄, age:18, gender:公

__slots__

__slots__取代了实例的__dict__字典

当一个类中变量很少,但是这个类的实例很多时,每一个实例都有一个属性字典会占用大量内存,此时__slots__可以帮助节省内存,但是属性字典的相关操作就限制了

‘‘‘
1.__slots__是什么:是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)
2.引子:使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)
3.为何使用__slots__:字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__
当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个
字典,这跟元组或列表很类似。在__slots__中列出的属性名在内部被映射到这个数组的指定小标上。使用__slots__一个不好的地方就是我们不能再给
实例添加新的属性了,只能使用在__slots__中定义的那些属性名。
4.注意事项:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该
只在那些经常被使用到 的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。
关于__slots__的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用__slots__可以达到这样的目的,但是这个并不是它的初衷。           更多的是用来作为一个内存优化工具。

‘‘‘
class Foo:
    __slots__=‘x‘

f1=Foo()
f1.x=1
f1.y=2#报错
print(f1.__slots__) #f1不再有__dict__

class Bar:
    __slots__=[‘x‘,‘y‘]

n=Bar()
n.x,n.y=1,2
n.z=3#报错
class Foo:
    __slots__=[‘name‘,‘age‘]

f1=Foo()
f1.name=‘alex‘
f1.age=18
print(f1.__slots__)

f2=Foo()
f2.name=‘egon‘
f2.age=19
print(f2.__slots__)

print(Foo.__dict__)
#f1与f2都没有属性字典__dict__了,统一归__slots__管,节省内存

__doc__

可以查看一个类的文档注释,无法被继承

__module__和__class__

__module__ 表示当前操作的对象在那个模块

__class__     表示当前操作的对象的类是什么

__del__

析构方法,当对象在内存中被释放时,自动触发执行。

当程序结束时,python只会回收自己的内存空间,即用户态内存,而操作系统的资源则没有被回收,这就需要我们定制__del__,在对象被删除前向操作系统发起关闭数据库链接的系统调用,回收资源

class Foo:

    def __del__(self):
        print(‘执行我啦‘)

f1=Foo()
del f1
print(‘------->‘)

#输出结果
执行我啦
------->

__call__

对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):

        print(‘__call__‘)

obj = Foo() # 执行 __init__
obj()       # 执行 __call__

__next__和__iter__实现迭代器协议

实现斐波那契数列

class Fb():
    def __init__(self):
        self.x = 0
        self.y =1
    def __iter__(self):
        return self
    def __next__(self):
        if self.y > 100:
            raise StopIteration("大于100,停止")
        self.x, self.y = self.y, self.x+self.y
        return self.x

f1 = Fb()
print(next(f1))
print(next(f1))
print(‘===>‘)
for i in f1: # iter(f1) -->  __iter__()  __next__()
    print(i)

描述符(__get__,__set__,__delete__)

参考

https://www.cnblogs.com/linhaifeng/articles/6204014.html

原文地址:https://www.cnblogs.com/dreamer-lin/p/11624512.html

时间: 2024-11-08 07:25:28

Python之旅(八)面向对象02的相关文章

Python学习笔记八 面向对象高级编程(一)

参考教程:廖雪峰官网https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 一.使用__slots__ 正常情况下,当定义了一个类之后,我们可以给这个类的实例绑定任何属性,这就是动态语言的优势: class Student(object): def __init__(self,name,age): self.name=name self.age=age bob=Student('Bob

Python学习笔记八 面向对象高级编程(二)元类

参考教程:廖雪峰官网https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 在廖老师的学习网站里"使用元类"这部分还是把我给看晕了...网上搜到一篇感觉讲的相对易懂一些,贴出链接:两句话掌握 Python 最难知识点--元类--以此文作为这篇笔记的源本. "道生一,一生二,二生三,三生万物" 1.在Python世界中,"type"即为道

python之旅:面向对象之继承与派生

一 初识继承 编写类时,并非总要从空白开始.如果你要编写的类正好是另一个现成类的特殊版本,可使用继承来减少代码冗余,子类会“遗传”父类的属性,从而解决代码重用问题 什么是继承 继承是一种创建新类的方式,新建的类可以继承一个或多个父类(python支持多继承),父类又可称为基类或超类,新建的类称为派生类或子类. 一个类继承另一个类时,他将自动获得另一个类的所有属性和方法:原有的类称为父类,而新类称为子类.子类继承了其父类的所有属性和方法,同时还可以定义自己的属性和方法. python中类的继承分为

Python之旅(七)面向对象

三大编程范式 三大编程范式(这三者各有千秋,不分好坏): 面向过程编程 函数式编程 面向对象编程 面向过程编程 “面向过程”(Procedure Oriented)是一种以过程为中心的编程思想. 过程是指解决问题的步骤.例如问题:如何把大象放入一个冰箱?步骤:先打开冰箱,在将大象放入冰箱,最后关上冰箱.面向过程是一种较机械式的思维方式. 优点: 复杂的问题流程化,进而简单化(一个复杂的问题,分成一个个小的步骤去实现,实现小的步骤将会非常简单). 性能比面向对象高,因为类调用时需要实例化,比较消耗

Python面向对象02/类的空间问题

Python面向对象02/类的空间问题 内容大纲 1.从空间角度研究类 2..类与类之间的关系 1.类的空间问题 1.1何处可以添加对象属性 在类的__init__可以添加,在类的方法也可以添加,在类的外部也可以添加 # class Human: # # mind = '有思想的' # # def __init__(self, name, age): # self.name = name # self.age = age # # def eat(self,argv): # # self.hobb

Python第十八课(面向对象基础)

Python第17课(面向对象基础)    >>>思维导图>>>中二青年 什么是继承? 继承是一种关系,描述两个对象之间,什么是什么的关系 例如麦兜,佩奇,猪刚鬣 都是猪啊, 在程序中,继承描述的是类和类之间的关系 例如a继承了b, a就能直接使用b已经存在的方法和属性 a称之为子类,b称之为父类,也称之为基类 为什么要使用继承 继承的一方可以直接使用被继承一方已经有的东西 其目的是为了重用已经有的代码,提高重用性 如何使用继承 语法 class 类名称(父类的名称):

Python基础篇(八)

key words:私有变量,类静态变量,生成器,导入Python模块,r查看模块可以使用的函数,查看帮助信息,启动外部程序,集合,堆,时间模块,random模块,shelve模块,文件读取等 >>> class Rectangle: ...     def __init__(self): ...         self.__width = 0 ...         self.__height = 0 ...     def setSize(self,width,height): .

开始 Python 之旅

开始 Python 之旅 课程来源 本课程基于 Python for you and me 教程翻译制作,其中参考了 Python tutorial 和 The Python Standard Library,并对原教程的内容进行了改进与补充. 相关链接地址如下: Python tutorial:http://www.pythondoc.com/pythontutorial3/index.html Python for you and me:http://pymbook.readthedocs.

【python】对象和面向对象

类的定义 python支持多重继承,在类名后面的小括号中,可以列出多个类名,以逗号分割. __init__方法在类的实例创建后被立即调用,注意与c++中构造函数不一样,因为对象在调用__init__时已经被构造出来,__init__方法不返回值,__init__方法不是必须要定义的. 每个类方法的第一个参数,包括__init__,都是指向类的当前实例的引用.按照习惯这个参数被称为self.在__init__方法中,self指向新创建的对象,在其他的类方法中,它指向方法被调用的类的实例.尽管当定义

python高级编程之超类02:super的缺陷

# -*- coding: utf-8 -*- # python:2.x __author__ = 'Administrator' #当使用多重继承层次结构时,再使用super的时候是非常危险的,主要是因为类的初始化,基类不在__init__中被隐式调用 #1滥用super和传统调用 #来自james knight(http://funm.net/super-harmful)示例中,类C使用__init__方法调用其基类,这样类B被调用2次 class A(object): def __init