python面向对象2

简单例子1

内建函数setattr() getattr() hasattr() 即使类中没有setattrgetattr魔术方法也不会报错 不同于len(A()), 如果类中没有len方法则会报错 setattr时,第2个位置的参数,可以是属性,也可以是方法,例如增加x属性,也可以是方法x。

 class Point:
def init(self, x, y):
self.x = x
self.y = y

def str(self):
return "Point({}, {})".format(self.x, self.y)

def show(self):
print(self)

p = Point(1, 2)

动态增加或修改

print('*******')
print(setattr(p, 'x', 99)) # None
print(setattr(p, 'z', 100)) # None
print(p.dict)

获取

print(getattr(p, 'y')) # 2
print(getattr(p, 'q', None)) # None
print(getattr(p, 'dict'))

{'x': 99, 'y': 2, 'z': 100},属性获取

print(p.dict)

{'x': 99, 'y': 2, 'z': 100}, 实例字典获取

判断

print(hasattr(p, 'a')) # False
print(hasattr(p, 'x')) # True

 # 综合例子

通过实例判断,获取属性

if hasattr(p, 'show'):
getattr(p, 'show')() # Point(99, 2)

getattr返回一个绑定方法show,调用show方法。

通过 实例 判断,动态增加属性(用的少)

if not hasattr(p, 'sub'):
setattr(p, 'sub', lambda point1, point2: Point(point1.x - point2.x, point1.y - point2.y))
print(p.sub(Point(1, 2, ), Point(3, 3))) # Point(-2, -1)

通过 类 判断,动态增加类属性

if not hasattr(Point, 'add'):
setattr(Point, 'add', lambda point1, point2: Point(point1.x + point2.x, point1.y + point2.y))

print(p.dict) # 实例字典中有sub方法
print(Point.dict) # 类字典中有add方法,没有sub方法

 # 只用来测试(不优雅)
class Point:
def init(self, x, y):
self.x = x
self.y = y

def str(self):
return "Point({}, {})".format(self.x, self.y)

def show(self):
print(self.x, self.y)

def len(self):
return 10

p = Point(1, 2)

感觉这两个差不多

print(Point.dict) # 类字典。魔术方法,存在类字典中
print(dir(Point)) # 父类和实例的各种方法及属性,包括魔术方法,不包括x, y, z ,有序列表

print(p.dict) # 只有x,y,实例字典只存放init中的内容
print(dir(p)) # 最全,父类和实例的各种方法及属性,包括魔术方法,包括x, y, z, 有序列表
print(p.dir()) # 同dir(p), 无序列表
print(Point.dir(p)) # 同dir(p), 无序列表。类调用方法(魔术和普通),都不会注入第一参数

p.dict['x'] = 99 # 相当于调用setattr方法
print(p.dict)

p.z = 100 # 相当于调用setattr方法
print(p.dict) # 增加一个z

print(p.z) # 相当于调用getattr方法

  • 魔术方法setattr getattr delattr getattribute

    setattr

    • 调用setattr 的情景

      init 函数中,self.x = x

      在类的外面 obj.x = 50

      在类的外面 setattr(obj, 'x', 50)

    • 不会调用setattr 的情景

    obj.dict['x'] = 50

    setattr 魔术方法

# 这个方法可以拦截对实例属性的增加,修改操作,如果设置生效,实例的字典为空 # 实例通过.点号设置属性,例如设置self.x = x属性,就会调用

class Point:
z = 6

def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y

def setattr(self, key, value):
self.dict.setitem(key, value)

等价于 self.dict[key] = value

p = Point(1, 2)
p.x = 20
print(p.dict) # {'x': 20, 'y': 2}因为初始化时也会调用2次
setattr

setattr , 但是如果实例的属性要加到实例的dict中, 需要自己手动完成, 如果不手动添加,则默认dict是清空的

如果只增加setattr方法,则实例字典会被清空

可以通过实例字典设置属性,但是取不出来属性,即无法使用obj.x

class Point:
z = 6

def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y

def setattr(self, key, value):
print("setattr {} = {}".format(key, value))

p = Point(1, 2)
print(Point.dict) # 有值
print(p.dict) # 如果只增加setattr方法,实例字典被空的

因为实例字典被清空了,而且也不会调用内键函数getattr(),所以报错

print(p.x) # 报错

print(p.y) # 报错

print(p.z) # 6

p.x = 50 # 还会调用setattr

print(p.x) # 刚设置完x属性,但还是取不到x属性,因为实例字典还是空的

print(p.dict) # {}

p.dict['x'] = 60 # 只有在实例的字典中添加x属性,才可以取到x属性,但是不会调用setattr
print(p.dict) # 字典中有值{'x': 60}

print(p.x) # 60

getattr

# 如果只增加getattr方法和原来没有什么变化,相当于没增加,一般不这么用
class Point:
z = 6

def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y

def getattr(self, item):
return "missing {}".format(item)

p = Point(1, 2)
print(Point.dict) # 有值
print(p.dict) # 如果只实现了getattr方法,实例字典中有值{'x': 1, 'y': 2}

print(p.x) # 1
print(p.y) # 2
print(p.z) # 6

p.m = 50 # 因为没有实现setattr方法,所以调用的是内键函数setattr()
print(p.m) # 50
print(p.dict) # 实例字典中有值{'x': 1, 'y': 2, 'm': 50}

p.dict['x'] = 60 # 只有在实例的字典中添加x属性,才可以取到x属性,但是不会调用setattr
print(p.dict) # 实例字典中有值x被改成{'x': 60, 'y': 2}
print(p.x) # 60

# getattr 魔术方法

查找属性顺序

instance.dict -> instance.class -> 继承类 —> object字典

class Base(object):
n = 0
z = 200

class Point(Base):
z = 6
x = 100

def init(self, x, y):
self.x = x
self.y = y

def getattr(self, item):
return "missing {}".format(item)

p = Point(1, 2)
print(Point.dict)
print(p.dict)

虽然父类没有实现setattr

但是object中实现了setattr

print(p.x) # 1
print(p.y) # 2

print(p.z) # 6
print(p.n) # 0
print(p.t) # missing 找了一圈找不到就会调用 getattr方法,

  • setattr getattr综合应用的例

  • 大多数情况都是这两种魔术方法同时使用

    # 其实不用实现这两个魔术方法,就可以满足开发需求
    lass Point:

    def init(self, x, y):
    self.x = x # 这时会调用setattr
    self.y = y

p = Point(1, 2)

print(p.x)  # 1
print(p.y)  # 2
p.z = 9
print(p.z)  # 9
# 如果想使用__setattr__ 和 __getattr__,则正常使用的例子
class Point:
 z = 6

 def __init__(self, x, y):
 self.x = x  # 这时会调用__setattr__
 self.y = y

 def __getattr__(self, item):
 return item

 def __setattr__(self, key, value):
 # 直接操作字典不会递归
 self.__dict__[key] = value


p = Point(1, 2)
print(Point.__dict__)  # 有值
print(p.__dict__)  # 有值


print(p.x)  # 1
print(p.y)  # 2
print(p.z)  # 6

p.x = 50  # 还会调用因为实现了__setattr__,所以会调用__setattr__,内部打印字符串就可以看到
print(p.x)  # 50
print(p.__dict__)  # 有值

p.__dict__['x'] = 60  # 实例的字典中添加x属性,但是不会调用__setattr__,内部打印字符串就可以看到
print(p.__dict__)   # 有值
print(p.x)  # 60

# 内键函数与魔术方法的区别例子

setattr()函数,当类中实现了setattr方法时,会走类中的这个方法

getattr()函数,当类中实现了getattr方法时,并且实例的字典中没有要获取的属性时,才会走类中的这个方法

class Point:
z = 6

def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y

def getattr(self, item):
print('111111')
return 'item: {}'.format(item)

def setattr(self, key, value):

直接操作字典不会递归

print('222222')
self.dict[key] = value

p = Point(1, 2)

p.x = 50 # 调用setattr
print(p.x) # 实例的字典中取到了50,没调用getattr
print(p.m) # 实例的字典中没有m属性,才会调用getattr

setattr(p, 'n', 99) # 调用setattr
print(p.n) # 取实例的字典中n属性,没调用getattr
print(p.m) # 实例的字典中没有m属性,才会调用getattr
print(getattr(p, 'n')) # 从字典中取
print(getattr(p, 't')) # 调用getattr

# 测试用例,正常代码不这么写
class Point:
z = 6

def init(self, x, y):
self.x = x # 这时会调用setattr
self.y = y

def getattr(self, item):
return "missing {}".format(item)

def setattr(self, key, value):
print("setattr {} = {}".format(key, value))

p = Point(1, 2)
print(Point.dict) # 有值
print(p.dict) # 空的

因为实例字典被清空了,但是类字典没有被清空,所以取不到x,y,但是能取到z

print(p.x) # missing x
print(p.y) # missing t
print(p.z) # 6

p.x = 50 # 还会调用setattr
print(p.x) # 刚设置完x属性,但还是取不到x属性,因为实例字典还是空的
print(p.dict) # {}

p.dict['x'] = 60 # 只有在实例的字典中添加x属性,才可以取到x属性,但是不会调用setattr
print(p.dict) # 字典中有值{'x': 60}
print(p.x) # 60

class Base:
n = 0 class Point(Base):
z = 6
d = {}
def init(self, x, y):
self.x = x # 这时会调用setattr
setattr(self, 'y', y)
self.dict['a'] = 5
def getattr(self, item):
print("missing {}".format(item))

return self.d[item]

def setattr(self, key, value):
print("setattr {} = {}".format(key, value))
self.d[key] = value
p = Point(1, 2)
print('+++++++++++++')
print(Point.dict)
print(p.dict)
print(p.x) # missing x 当找到类的字典时,'d'下面有'x', 但是外面没有'x', d': {'x': 1, 'y': 2}
print(p.z) # 6
print(p.n) # 0
print(p.t) # missing t
print(p.a) # 5

delattr( 了解)

class Point:
z = 6

def init(self, x, y):
self.x = x
self.y = y

def delattr(self, item):

return print('Can not del {}'.format(item))

p = Point(1, 2)
del p.x
p.z = 15
del p.z

不能删除了

getattribute(了解)

实例的所有属性访问,第一个都会调用这个方法,它阻止了属性的查找顺序,

该方法应该返回计算后的值,或者抛出一个Attribute Error异常

  1. 它的return值将作为属性查找的结果

  2. 如果抛出AttributeError, 则会直接调用 getattr方法,因为属性没有找到

属性查找顺序

# instance.getattribute ->instance.dict -> instance.class -> 祖类 —> object字典

使用方法

# getattribute方法中为了避免出现无限递归,它的实现应该永远调用基类的同名方法以访问需要的任何属性,例如:object.getattribute(self, name),

一般不要使用这个方法

class Point(Base):
z = 6

def init(self, x, y):
self.x = x
self.y = y

def getattr(self, item):
return("missing {}".format(item))

def getattribute(self, item):

return object.__getattribute(self, item)

return super().getattribute(item)

p = Point(1, 2)
print('+++++++++++++')
print(Point.dict)
print(p.dict)

print(p.x) # 1
print(p.z) # 6
print(p.n) # 0
print(p.t) # missing t

增加子类功能的3种方法

1、直接继承然后修改,2、给子类增加一个装饰器, 3、Mixin Mixin就是其他类混合进来,同时带来了类的属性和方法,本质上是多继承,是一种组合的设计模式。和装饰器比较,两者的装饰效果一样,但是Mixin是类,可以继承。 Mixin类的使用原则:

  • Mixin类中不应该显示的出现init方法

  • Mixin类通常不能独立工作,因为它是准备混入别的类中,实现部分功能

  • Mixin类的祖先类也应该是Mixin类

    # 直接继承
    class Document: #因为有print方法未实现,所以称为抽象基类
    def init(self, content):
    self.content = content

    def print(self):
    raise NotImplementedError

class Word(Document):
 def print(self):
 print(self.content, 'word style')


class Pdf(Document):
 def print(self):
 print(self.content, 'pdf style')

class Test(Document): #在python中,这个子类可以不实现父类中的print方法,
 # 但是在其他语言中,如果继承自父类,但是父类的抽象方法,未能全部实现,
 # 则这个子类也属于抽象类,抽象类不可以实例化
 pass


w = Word('tutu')
w.print()

#装饰器
class Document:
 def __init__(self, content):
 self.content = content


class Word(Document):
 pass

# 装饰器1
def printable(cls):
 def wrapper(content):
 cls.print = lambda: print(content)
 return cls
 return wrapper

# 装饰器2
def printable(cls):
 def wrapper(content):
 def fn():
 print(content)
 cls.print = fn
 return cls
 return wrapper

# 装饰器3 **********************************
def printable(cls):
 def fn(self): # 这3行等价于 cls.print = lambda self: print(self.content)
 print(self.content)
 cls.print = fn
 return cls

# 装饰器4,注意使用print时,不要出现递归
def printable(cls):
 def _print(self):  # 写在类的外面的,但是需要给类添加的方法,第一个参数必须为self
 print(self.content)
 cls.print = _print
 return cls

@printable  #PrintableWord = printable(PrintableWord)
class PrintableWord(Word):pass

p = PrintableWord('abs')
p.print()

# Mixin,多继承
# PrintableMixin一般值需要里面的print方法,里面一般不写__init__
class Document:
 def __init__(self, content):
 self.content = content

class Word(Document):
 pass

class PrintableMixin:
 def print(self): #这个两个print不会出现递归,因为第一个print相当于有一个隐藏的名字,
 # 只能通过这两种方法调用,PrintableMixin.print(instance)  p.print()
 print(self.content)

class PrintableWord(PrintableMixin, Word):pass

p = PrintableWord('abs')
p.print()
#print(PrintableMixin.__dict__)


# Mixin增强
class Document:
 def __init__(self, content):
 self.content = content

class Word(Document):
 pass

# Mixin功能,多继承
class PrintableMixin:
 def print(self):
 print(self.content)

class SuperPrintableMixin(PrintableMixin):
 def print(self):
 print('增强之前打印:', self.content)
 super().print()
 print('增强之后打印:')

class PrintableWord(SuperPrintableMixin, Word): pass

p = PrintableWord('abs')
p.print()

python2.7与python本质区别(了解)

python2.7中与python3的不同: 都不同python3 type(A), dir(A) a.class

dir() 函数的用法 dir(A) 收集对象的属性

模块化

模块化是组织代码的方式,封装代码,封装就是边界

在其他编程语言中,库、包、模块是同一种概念,是代码组织方式

在python中:

  • 模块module:python文件

  • 包package:是指模块组织在一起的,和包同名目录及其相关文件

导入语法

  • import 只能加模块名

    # 完全限定名称访问path
    import os.path # 导入os.path, os加入当前名字空间
    print(dir()) # 里面只有os,没有os.path

  • import as

    import os.path as osp # 导入os.path并赋值给osp
    print(dir()) # 里面只有osp

  • 局部导入

    def testimport():
    import os.path
    print(dir()) # os存在里面
    testimport()
    print(globals().keys()) # os不存在里面

  • 总结

    • 导入顶级模块,其名称会加入到本地名词空间中,并绑定到其模块对象

    • 导入非顶级模块,只将其顶级模块名称导入到本地名词空间。导入的模块必须使用完全限定名称来访问。

    • 如果使用了as, as后的名称直接绑定到导入的模块对象,并将该名称加入到本地名词空间

  • 其他例子:

    from functools import wraps as wr, partial
    print(dir()) # [..., 'wr', 'partial']

    四种方式获得同一个对象的 exists

    import os
    print(os.path.exists)
    print(exists)
    print(os.path.dict['exists']) # 字符串,os.path被导入之后,可以取dict
    print(getattr(os.path, 'exists')) # 字符串, module对象也可以使用getattr来获得属性

  • from 必须加模块 import 类名,函数名,模块名

    from pathlib import Path # 导入类Path
    print(Path, id(Path))
    import pathlib as p1
    print(p1.Path, id(p1.Path)) # p1.Path 与 Path 是同一个对象,id相同

自定义模块

模块名:由字母数字下划线组成,不能以数字开头,不能有中文

不要使用系统模块名,以避免冲突,通常模块名为全小写,下划线来分割。

# test1.py

class A:
def showmodule(self):
print(1, self.module, self)
print(2, self.dict)
print(3, self.class.dict)
print(4, self.class.name)

a = A()
a.showmodule() # self.module = 'main'

test2.py

import test1
a = test1.A()
a.showmodule() # self.module = 'test1'

test3.py

from test1 import A as cls
a = cls()
a.showmodule() # # self.module = 'test1'

模块搜索顺序

会从sys.path中,从前到后依此查找,并不搜索这些目录的子目录

路径可以是字典、zip文件、egg文件。egg文件是由setuptools库创建的包, 第三方库常用的格式。zip文件是添加了元数据(版本号,依赖项等)信息的文件

print(*sys.path, sep='\n')

sys.path路径搜索顺序为

  • 程序主目录,程序运行主程序脚本所在的目录文件

  • PYTHONPATH目录,环境变量PYTHONPATH设置的目录

  • 标准库目录,python自带的库模块所在的目录

  • 第3方库

模块会出现重复导入嘛?

模块不会存在重复导入的现象,所有加载的模块都会记录在sys.modules 中,

sys.modules是存储已经加载所有模块的字典,里面包含os, os.path

# test1.py

class A:
def showmodule(self):
print(1, self.module, self)
print(2, self.dict)
print(3, self.class.dict)
print(4, self.class.name)

a = A()
a.showmodule()

test2.py

import test1
print('*****')
import test1 # 只能看到一次1,2,3, 4的打印,只有第一次导入打印了,
print(sys.modules) # 可以看到test1在里面了。

name main

解释器初始化时,会初始化sys.modules字典,然后加载builtins模块、main模块、sys模块,及sys.path

name

  • 每个模块都有这个特殊变量来存储当前模块的名称,如果不指定,则默认为源代码文件名,如果是包则有限定名。import导入模块,name默认是模块名。

  • 当从标准输入(命令行方式巧代码)、运行脚本($ python test.py)、交互式读取时,这3种情况都会将name设置为main,模块的顶层代码在main,这个作用域中执行

if name == 'main':
print('in main')
else:
print('in imported module')

if name == 'main':作用

  • 对于非主模块,测试本模块内的函数、类

  • 避免主模块变更的副作用

    顶层代码没有封装,主模块使用时没有问题。但是,一旦有了

    新的主模块,旧的主模块成了被导入模块,由于原来代码没有封装,一并执行了

模块属性

file 源文件绝对路径,字符串
name 模块名
package 如果模块是包,同name; 否则可以设置为顶级模块的空字符串
spec 显示模块规范
cached 编译后的字节码文件路径,字符串

在m1文件中导入m2,并且查看m2属性的两种方法

import m1.m2 as m2 # m1下面同时存在m2模块和m2包,找到的是m2包

print(dir(m2)) # 获得m2的属性名,返回列表,如果在m2里面的init文件中,加入属性B,则可以看到
print('************')
for name in dir(m2):
print(getattr(m2, name))

m2模块相当于一个字典,从字典中获得name属性。例如:

name doc file

等价于

print('***********')
for k, v in m2.dict.items():
print(v)

问题:如果m2中有变量A,为什么dir(m2),看不到A,如果在m2中输入dir(),就可以看到A?因为先找到的是m2包

  • 总结:

# 如果想在目录下面写代码,需要写入目录下的初始化文件中

当目录作为模块使用,则会执行初始化文件中的代码

模块不是包,但是,包是一种特殊的模块

模块就是命名空间,其内部的顶层标识符,都是它的属性,可以通过dict或者dir(module)查看。

  • 模块的加载与加入到命名空间的对比

    import sys
    import m.m1.m2
    print(dir()) # 只有'm'
    print(sorted(filter(lambda x: x.startswith('m'), sys.modules.keys())))

    'm', 'm.m1', 'm.m1.m2'都在,依次加载m,m1,m2的初始化文件

    from os import stat
    print(dir()) # 没有os

    print(os.path()) # 为什么用不了?os没在当前的命名空间,虽然os已经被加载过了

    改成

    import os.stat
    print(os.path) # 可以使用因为os,加入到了当前命名空间了

  • 绝对导入与相对导入

    包内部各个模块之间相互调用,建议使用相对导入

    在包外部使用,包内部模块,建议使用绝对导入

    • 绝对导入:import 或者from语句后面没有. 绝对导入的搜索顺序是,

      先到sys.modules中查看是否已经被加载,被加载直接取出,如果没有被加载,则按照sys.path中的顺序搜索,然后加载到sys.modules模块中,sys.path的搜索顺序是当前运行模块的根路径,PYTHONPATH路径,标准库和第三方库。

    • 相对导入:

      • 只能from语句后面有.

      • 1个点表示当前目录内

      • 2个点表示上一级目录

      • 3点表示上上级目录

      • 一旦一个模块中使用相对导入,就不可以作为主模块运行了

      • 相对导入是为了保证包内部资源的相互引用,而不是为了直接运行,正确的使用方式是在顶层模块中使用这些包。

    • all

    在模块m11中定义普通变量、保护变量、私有变量、特殊变量:

    A = 5
    _B = 6
    __C = 7
    my = 8

    在模块m1中导入模块m11

    import m11
    print(m11.A, m11._B, m11.__C, m11.my) # 没有被隐藏都可以访问

    也就是说模块内所有变量都不做特殊处理

    如果导入时使用*:

    # 在m1模块中写入代码,并且运行m1
    from m11 import * # 问题:问什么使用.m11就不可以呢?
    print(dir()) # 只能看到普通变量A,带下划线的都看不到了

    如果在模块m11中加入all, 则all中有什么,命名空间就有什么变量

    all是一个列表,里面元素是字符串,每个元素只能是当前模块中的属性名。

    all = ['A', '_B']
    A = 5
    _B = 6
    __C = 7
    my = 8

    在m1中运行

    from m11 import *
    print(dir()) # ['A', '_B']

  • 例子:从顶层模块访问包中属性的方法

    如果使用了*, 就尽量使用all, 避免导入过多的模块,而产生冲突

    一般使用from module import name1, name2

    已知:

    # m 的init.py中写入
    print(name)
    x = 5

    m1文件中写入

    print(name)
    y = 5

    问题:顶层模块test.py如何访问到m1中的y?

    # 方法1

    在test文件中写入

    from m import m1
    print(m1.y)

# 方法2(不可以因为m.m1没在命名空间,容易错的导入)
import m 
print(m.m1.y)


# 方法3
# 在m的__init__文件中写入 ,__all__ = ['x', 'm1'], __all__中包含的字符串名字是__init__.py中的属性名,和m包下面的模块名m1.
from m import *
print(dir())  # ['x', 'm1']
print(m1.y)

# 方法4
# 在m的__init__文件中写入 
# from . import m1
from m import *  # __init__.py中有什么变量,就导入了什么变量
print(m1.y)
  • 模块变量的修改

    模块对象是同一个,因此模块的变量也是同一个,对模块变量的修改会影响所有的使用该模块使用者,特别是常量,引用类型的还好。

    另外可以通过猴子补丁的方式修改模块的变量、类、函数。

打包分发

使用setup.py打包

在项目根目录下建立一个setup.py文件(和m包同级),在里面填写脚本

from distutils.core import setup
setup(name='m',
version='1.0',
description='Python test m',
author='JW',
author_email='',
url='',
packages=['m', 'm.m1', 'm.m1.m2'],
)

packages里面填写,所有包名

创建源代码分发包

# 在项目根目录下生成dist文件里面有m-1.0.tar.gz
$ python setup.py sdist

安装写好的包,方法1

$ pip install m-1.0.tar.gz

安装写好的包,方法2

先解压,里面含有setup.py文件

$ python setup.py install

对于需要编译的语言,需要先编译再安装

$ python setup.py build

会在根目录下生成一个build文件夹,在lib下是编译好的文件

setup.py生成各种压缩包的命令

 python setup.py bdist --format=rpm
python setup.py bdist --format=egg
python setup.py bdist_wheel

插件化开发

参考笔记10模块化03插件化开发.pdf

面向对象补充

未实现和未实现异常

# NotImplemented, 是NotImplementedError实例,是个单值,类似与None

NotImplementedError,是一个类

print(type(NotImplemented)) #
print(type(NotImplementedError)) #

slots魔术方法

# slots内存不够用时使用,需要很多实例,例如:点类,适用属性和值都简单的类,

使用slots
不可以动态增加属性
实例没有dict属性了
不影响继承实例的字典
class A:
x = 1
slots = ('y', 'z') # 一般用元组

slot = 'y', 'z' # 与上面等价

slot = ['y', 'z']

slot = 'y' # 也是一个元组

def init(self):
self.y = 5
self.z = 10

def show(self):
print(self.x, self.y)

a = A()
a.show()
print('A:', A.dict)

print('a:', a.dict) 没有这个属性了

slots不影响继承

# 继承
class A:
x = 1
slots = ('y', 'z') # 一般用元组

slot = 'y', 'z' # 与上面等价

slot = ['y', 'z']

slot = 'y' # 也是一个元组

def init(self):
self.y = 5
self.z = 10

def show(self):
print(self.x, self.y)

class B(A):
pass

print('********')
b = B()

print('B:', b.dict) # {}

内建函数tracemalloc

tracemalloc -Trace memory allocations

# 内建模块,具体信息参考官方文档
import tracemalloc

tracemalloc.start() # 开始跟踪内存分配

d = [dict(zip('xy', (5, 6))) for i in range(1000000)] # 237M
t = [tuple(zip('xy', (5, 6))) for i in range(1000000)] # 191M

snapshot = tracemalloc.take_snapshot() # 快照,当前内存分配
top_stats = snapshot.statistics('lineno') # 快照对象统计,按行统计内存

top_stats = snapshot.statistics('filename') # 参看当前文件总内存

for stat in top_stats:
print(stat)

运算符重载中的反向方法

add 与iadd

class A:
def init(self, x):
self.x = x

def add(self, other):
print('add')
return self.x + other.x

def iadd(self, other):
print('iadd')
return A(self.x + other.x)

def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
raise NotImplementedError

def str(self):
return str(self.x)

repr = str

print('***********')
a = A(4)
c = A(5)
print(a + c) # add 9
print(c + a) # add 9

a += c # iadd 9
print(a)
c += a # iadd 9
print(c)

调用iadd的情况,B类中没有实现add方法,A类中实现了

radd方法,想计算b + a

# 一种实现
class A:
def init(self, x):
self.x = x

def add(self, other):
print('add')
return self.x + other.x

def iadd(self, other):
print('iadd')
return A(self.x + other.x)

def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
raise NotImplementedError

def str(self):
return str(self.x)

repr = str

class B:
def init(self, x):
self.x = x

def add(self, other):
if isinstance(other, type(self)):
return self.x + other.x
else:
return NotImplemented

a = A(2)
bb = B(3)
print('*************')
print(bb + a) # bb.add(a) 找到NotImplemented, 所以反转计算a + bb,近而调用a.radd(bb)

print('+++++++++++')
bb += a # bb没有iadd 找到add, 抛出NotImplemented,所以找到a.radd
print(bb)

另一种实现

class A:
def init(self, x):
self.x = x

def add(self, other):
print('add')
return self.x + other.x

def iadd(self, other):
print('iadd')
return A(self.x + other.x)

def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
raise NotImplementedError

def str(self):
return str(self.x)

repr = str

class B: # 默认未实现add
def init(self, x):
self.x = x

a = A(2)
bb = B(3)

print(bb + a)
print('+++++++++++')
bb += a
print(bb)

实现 1 + a

# 1 + a 方法1
class A:
def init(self, x):
self.x = x

def add(self, other):
print('add')
return self.x + other.x

def iadd(self, other):
print('iadd')
return self.x + other.x

def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
elif isinstance(other, int):
return self.x + other
else:
raise NotImplemented

def str(self):
return str(self.x)

repr = str

a1 = A(3)
print(1 + a1) # 1.add(a), 但是这个加法的返回值是NotImplemented,

解释器发现是这个值,就发生反转相当于计算a1 + 1,所以就从a对象找radd

方法2

# 如果不是整型,返回自己, 1+a 另一种实现
class A:
def init(self, x):
self.x = x

def add(self, other):
print('add')
return self.x + other.x

def iadd(self, other):
print('iadd')
return self.x + other.x

def radd(self, other):
print('radd')
if hasattr(other, 'x'):
return self.x + other.x
else:
try:
other = int(other)
except ValueError:
other = 0
return self.x + other

def str(self):
return str(self.x)

repr = str

a1 = A(8)
print(1 + a1)
print('abc' + a1)

python中的对象模型

python中任何对象都有类型,可以使用type() 、class查看

  • 继承所有类都继承自object,type类也继承自object,object的父类是空

  • python中所有类的类型都是type(元类),实例的类型是生成实例的类。

类型与继承.png

无参构造与值

print(None)
print(Exception) # 无参构造是一个类
print(NotImplemented)

你可能感兴趣的:(python面向对象2)