继承 多态 封装 及内置函数

**什么时候用组合什么时候用继承:**
老师类 和 生日类 老师的生日 老师.生日 用组合

老师类 和 人类 老师是人 class老师类(人类) 用继承 另外,有相同属性也可判断要用继承

### 组合
一个对象的属性值是另外一个类的对象
一个类的属性 用另一个类的对象来描述
两连点:alex.weapon.hand18(jin)
alex的weapon属性,因为是一个另类的对象,weapon也有自己的方法,故两连点
```python
# 人狗大战
class Dog:
def __init__(self,name,aggr,hp,kind):
self.name = name
self.aggr = aggr #武力值
self.hp = hp #血
self.kind = kind
#咬
def bite(self,person):
person.hp -= self.aggr

class Person:
def __init__(self,name,aggr,hp,sex):
self.name = name
self.aggr = aggr
self.hp = hp
self.sex = sex
self.money = 0

def attack(self,dog):
dog.hp -= self.aggr

#获取装备
def get_weapon(self,weapon):
if self.money >= weapon.price:
self.money -= weapon.price
self.weapon = weapon
self.aggr += weapon.aggr
else:
print("余额不足,请先充值")
#装备
class Weapon:
def __init__(self,name,aggr,njd,price):
self.name = name
self.aggr = aggr #武力值
self.njd = njd #耐久度
self.price = price #价格

#技能:18掌
def hand18(self,person):
if self.njd > 0:
person.hp -= self.aggr * 2
self.njd -= 1

alex = Person('alex',0.5,100,'不详')
jin = Dog('金老板',100,500,'teddy')
w = Weapon('打狗棒',100,3,998) #创造武器w
# alex装备打狗棒
alex.money += 1000
alex.get_weapon(w) #alex获取武器w
print(alex.weapon)
#self.weapon = weapon即传入的w Alex的武器属性是武器的对象
# alex.weapon 是 Weapon类的对象
print(alex.aggr)
alex.attack(jin)
print(jin.hp)
alex.weapon.hand18(jin) #武器的大招,传入jin jin掉血
print(jin.hp)
```


## 面向对象三大特性:继承 多态 封装
### 继承
```
class A:pass # 父类,基类,超类
class B(object):pass # python3中,不写(类)默认继承object 里面很多双下方法
class A_son(A):pass # 子类,派生类
class AB_son(A,B):pass # 子类,派生类 多继承
# 一个类 可以被多个类继承
# 一个类 可以继承多个父类 —— python里

print(A_son.__bases__) #查看继承自谁
print(AB_son.__bases__)
print(A.__bases__) # >>>object python3 -新式类# 没有继承父类默认继承object
```

避免大量重复代码
动物定义所有动物的共性
人猪狗继承自动物,各自有各自的方法,这些方法是人猪狗都有的
具体的实例继承自人或猪或狗,再添加自己的独特属性

扫描二维码关注公众号,回复: 7015873 查看本文章

面试题:当子类没有init,但父类子类都有func方法,若子类实例化,调用的是谁的func
```python
# 狗类 吃 喝 看门(guard)
# 鸟类 吃 喝 下蛋(lay)
class Animal:
def __init__(self):
print('执行Animal.__init__')
self.func()
def eat(self):
print('%s eating'%self.name)
def drink(self):
print('%s drinking'%self.name)
def func(self):
print('Animal.func')

class Dog(Animal):
def guard(self):
print('guarding')
def func(self):
print('Dog.func')
dog = Dog() #自己没有__init__ 就用父类的
#Dog实例化触发父类__init__时,执行了self.func(),而父类子类都有func
#因为在Dog实例化时,先触发__new__生成Dog的self,然后才去找__init__,进而找父类
#父类要求执行self.func,而dog里是有func的,自然调用自己的
```

#### 单继承 后 派生新属性和方法
父类 子类都有初始化方法,但是子类的初始化里 会让父类执行init,进而让子类的self获取父类中的属性和方法
除此之外,子类中初始化的其他属性和新定义方法就是派生的属性和方法
**方法1 父类名.__init__(self, 属性1,属性2)**
```python
class Animal:
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp

def eat(self):
print('吃药回血')
self.hp+=100

class Dog(Animal):
def __init__(self,name,aggr,hp,kind):
Animal.__init__(self,name,aggr,hp) #这里的self全程都是Dog的self
#若没有这句,狗就只有kind属性,因为自己有__init__
#不会去找父类,进而无法初始化出self.name = name等

self.kind = kind # 派生属性:原来属性基础上添加的属性
def eat(self):
Animal.eat(self) # 如果想在父类eat方法基础上添加新内容,需要在子类中调用父类,再加
self.teeth = 2
def bite(self,person): # 派生方法:原来属性基础上添加的方法
person.hp -= self.aggr

jin = Dog('金老板',100,500,'吉娃娃')
jin.eat()
print(jin.hp)

class Person(Animal):
def __init__(self,name,aggr,hp,sex):
Animal.__init__(self,name,aggr,hp)
self.sex = sex # 派生属性
self.money = 0 # 派生属性

def attack(self,dog):
dog.hp -= self.aggr

def get_weapon(self,weapon):
if self.money >= weapon.price:
self.money -= weapon.price
self.weapon = weapon
self.aggr += weapon.aggr
else:
print("余额不足,请先充值")
alex = Person('alex',1,2,None)
alex.eat()
print(alex.hp)

jin.bite(alex)
print(alex.hp)
# 父类中没有的属性 在子类中出现 叫做派生属性
# 父类中没有的方法 在子类中出现 叫做派生方法
# 只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,若父类爷爷类也没有则报错
# 如果父类 子类都有的 用子类的

# 如果想用父类的再加新内容,单独调用父类的:
# 方法1: 父类名.方法名 需要自己传self参数
# 方法2: super().方法名 不需要自己传self
(父类实例化后调用父类__init__方法,当然不用再传self)
# 正常的代码中 单继承 === 减少了代码的重复
# 继承表达的是一种 子类是父类的关系
```

**方法2 super().__init__(属性1,属性2) super单继承很好用**
```python
class Animal:
def __init__(self,name,aggr,hp):
self.name = name
self.aggr = aggr
self.hp = hp
def eat(self):
print('吃药回血')
self.hp+=100

class Dog(Animal):
def __init__(self,name,aggr,hp,kind):
super().__init__(name,aggr,hp) # 只在新式类中有,python3中所有类都是新式类
#super(Dog, self).__init__(name,aggr,hp) ()中默认传了本类即Dog的self
#父类实例化后调用父类__init__方法时,不用传self,只需传其他参数

self.kind = kind # 派生属性
def eat(self):print('dog eating')

# jin = Dog('金老板',200,500,'teddy')
# print(jin.name)
# jin.eat()
super(Dog,jin).eat() #super在类外面也能用,调用的是父类的eat方法
```

#### 多继承
以下全是python3的
**mro: 能直接查看找的顺序,新式类才有mro**
**就连super() 也是严格按照mro顺序查找的**
总结:有共同父类则广度优先,没有共同父类则深度
钻石继承:广度优先情况 (两条路都能找到A则先找BC)
B(A) , C(A) , D(B,C)
D.func时,D没有func时,优先找B,B没有找C,C没有最好找A
D先找左边父类,再找右边父类,再找爷爷类

漏斗问题
只有B才是A的子类,B找了不去找A ,去找C的话就找不到了

小乌龟问题(两条路都能找到F,且是太爷爷,则F最后找)

```python
#
# 新式类: 继承object类的才是新式类 广度优先 python3全是新式类
# 经典类: 在2.7中直接创建一个类就是经典类 深度优先

# 新式类才有mro ,能直接查看顺序
# print(D.mro())
# D.mro()

# 单继承 : 子类有的用子类 子类没有用父类
# 多继承中,我们子类的对象调用一个方法,默认是就近原则,找的顺序是什么?
# 经典类中 深度优先
# 新式类中 广度优先
# python2.7 新式类和经典类共存,新式类要继承object
# python3 只有新式类,默认继承object
# 经典类和新式类还有一个区别 mro方法只在新式类中存在
# super 只在新式类存在
# super的本质 :不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的
```

加上super的多继承问题
证明:super的本质 不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的

结果为:打印A C B D

多继承用super()时,super()是按照mro列表的顺序来查找父类
按道理图中A类里super()应该去找object了,但不是,是按照C.mro的顺序找的

### 多态
其他强类型语言的多态:
定义A类 并有一个x方法
B类继承自A类 有x方法,并写了x的具体实现方式
C类继承自A类 有x方法,并写了x的具体实现方式
B或者C的实例虽然调用的都是x方法,但是不同实例调用结果不同
其他语言需要有个A类,来让B类C类知道是一家子,x是关联的

python的多态:不需要先定义A类,但需要定义x方法,且 x(obj) 里面会调用 真实实例的x方法,即B或C中具体的x方法

### 封装 的思想
封装:打包(类)+保密(私有__属性)
把一个包含了很多私有属性和私有方法的类 写好后,可在别的.py文件中把它导入进来

单下划线_和双下划线__的约定(仅仅是约定,实在带下划线调用其实可以调到的):
1. 单表示隐藏起来的属性,类的外部不能用(封装要明确区分内外)
2. 双下滑线开头的属性,python会重命名属性;例如AAA类中的__xxx,会重命名为_AAA__xxx
只是原来的属性名无法调用,并不是真的无法访问

例1
```python
# 广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种
# 只让自己的对象能调用自己类中的方法

# 狭义上的封装 —— 面向对象的三大特性之一
# 属性 和 方法都藏起来 不让你看见
class Person:
__key = 123 # 私有静态属性
def __init__(self,name,passwd):
self.name = name
self.__passwd = passwd # 私有属性

def __get_pwd(self): # 私有方法
return self.__passwd #只要在类的内部使用私有属性,就会自动的带上_类名

def login(self): # 正常的方法调用私有的方法
self.__get_pwd()

alex = Person('alex','alex3714')
print(alex._Person__passwd) # _类名__属性名 alex.__dict__中也能看到
print(alex.get_pwd())
# 所有的私有 都是在变量的左边加上双下划綫
# 对象的私有属性
# 类中的私有方法
# 类中的静态私有属性
# 所有的私有的 都不能在类的外部使用
```
例2
```python

__author__ = 'Linhaifeng'

class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high

def tell_area(self): #此时我们想求的是面积
return self.__width * self.__length *self.__high

def tell_width(self):
return self.__width


r1=Room('卫生间','alex',100,100,10000)

# arear=r1.__width * r1.__length #会报错,只能在类里才能用双下__直接访问该函数
print(r1.tell_area())
```
例3
```python
#为外部修改私有属性 提供方法
class Room:
def __init__(self,name,length,width):
self.__name = name
self.__length = length
self.__width = width
def get_name(self):
return self.__name
def set_name(self,newName):
if type(newName) is str and newName.isdigit() == False:
self.__name = newName
else:
print('不合法的姓名')
def area(self):
return self.__length * self.__width

jin = Room('金老板',2,1)
print(jin.area())
jin.set_name('2')
print(jin.get_name())
```
私有属性补充
父类的私有属性 不能被子类调用
```python
class Foo:
__key='123'

class Son(Foo):
print(Foo.__key)

#用到私有属性的场景
1.隐藏属性,不想被类外部调用
2.想保护这个属性,不想被随意改变
3.保护此属性,不被子类继承
```
### 设计模式(编程思想):接口类 抽象类
共有目的:规范子类,都是面向对象的开发规范
python中原生没有接口类说法,因为自带多继承,可用class模拟实现接口类

java不支持多继承,但借助Interface 可以实现多继承概念,故有了接口类的说法

首先看类的多继承

接口类和接口不是一回事

**接口类**
(多继承不好控制)
每个类要实现不同功能,即便是相同功能,每个类的实现方式也不同
默认多继承,接口类中的所有的方法都必须不能实现

@abstractmethod : 规定子类必须有下句的方法,方法里内容pass就行了,具体实现 在具体的Tiger类里实现即可

例如tiger类继承自 必须有走方法的类 和 必须有 游方法的类,那么tiger类必须定义走方法和游方法,否则,当tiger实例化时,会报错提醒 少写了某个必要方法

接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些不需要的接口

**抽象类**
设计模式:抽象类
不支持多继承,抽象类中方法可以有一些代码的实现
解决 多个 实现功能很相近的类

两种思想的比较:
一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
多继承的情况 由于功能比较复杂,所以不容易抽象出 相同功能的具体实现 去写在父类中
```python
#一切皆文件
import abc #利用abc模块实现抽象类

class All_file(metaclass=abc.ABCMeta):
all_type='file'
@abc.abstractmethod #定义抽象方法,无需实现功能
def read(self):
'子类必须定义读功能'
with open('filaname') as f:
pass

@abc.abstractmethod #定义抽象方法,无需实现功能
def write(self):
'子类必须定义写功能'
pass

class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('文本数据的读取方法')
def write(self):
print('文本数据的读取方法')

class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('硬盘数据的读取方法')

def write(self):
print('硬盘数据的读取方法')

class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
def read(self):
print('进程数据的读取方法')

def write(self):
print('进程数据的读取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#这样大家都是被归一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)
# 抽象类 : 规范
# 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
# 多继承的情况 由于功能比较复杂,所以不容易抽象出 相同功能的具体实现 去写在父类中

# 抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化
# java :
# java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
# 但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题

# python
# python中没有接口类 :
# python中自带多继承 所以我们直接用class来实现了接口类
# python中支持抽象类 : 一般情况下 单继承 不能实例化
# 且可以实现python代码
```

### 内置方法
**内置方法:property(静态属性) classmethod(类方法) staticmethod(静态方法)**
1.@property 作用:obj.方法() 得出结果 上一行加了@property后,可将方法伪装成属性,以后调用直接用 obj.方法

但有限制:这个方法() 中,不能跟参数

且不能直接改:obj.方法=值 会报错
有时候明明是个方法 动词,却得到的是个数值之类的 名词 可伪装成静态属性
如果想改,加@name(self, new_name)且只能是一个参数

删除由方法伪装的属性
del 触发deleter方法 ,deleter下面是打印就是打印,有删除就删除,结果由写的代码定

2.staticmethod(静态方法)

类中的方法需要传self,若需要在实例化之前不传任何对象,即可完成该函数功能,可以用静态方法
直接 类名.函数()即可完成功能的实现

3.classmethod(类方法)

一般函数传的是self,self为实例,直接去改改的是实例属性,但若是在没有生成对象情况下,让类直接使用方法,不需要对象即可操作值
当这个方法的操作只涉及静态属性使时,就应该使用classmethod来装饰此方法

总结:

类方法和静态方法 都是类调用的
对象可以调用类方法和静态方法,一般情况下 推荐用类名调用
类方法 有一个默认的参数 cls 代表这个类 cls
静态方法 没有默认的参数 就像函数一样

### 自省 反射
```python
#自省 反射
hasattr(obj,'属性') #obj.属性 是否存在
getattr(obj,'属性') #有则返回obj.属性 不存在则报错
getattr(obj,'属性','默认值') #有则返回obj.属性 不存在不会报错,返回那个默认值
setattr(obj,'属性','属性的值') #obj.属性=属性的值
delattr(obj,'属性') #del obj.属性
#注意:反射可以反射类的属性,
hasattr(cls,'属性')等等 一切皆对象,类也是对象
内置模块也能用
反射自己当前模块的变量:print(getattr(sys.module['__main__'],'属性'))
反射自己当前模块的函数:getattr(sys.module['__main__'],'函数'))()
如果是被导入,又想反射被导入模块中的属性:getattr(sys.module[__name__],'属性') 防止被写死
模块在不被导入时叫'__main__' , 如果是被导入,则叫这个模块的名字,而sys.module可以查看所有导入进来的模块,且为字典
```

使用场景:
反射其实就是对属性的增删改查,但是如果直接使用内置的dict来操作,语法繁琐,不好理解另外一个最主要的问题是,如果对象不是我自己写的是另一方提供的,我就必须判断这个对象是否满足的要求,也就是是否我需要的属性和方法

框架设计方式:
框架代码:
```python
"""
反射被称为框架的基石,为什么
因为框架的设计者,不可能提前知道你的对象到底是怎么设计的
所以你提供给框架的对象 必须通过判断验证之后才能正常使用
判断验证就是反射要做的事情,
当然通过__dict__也是可以实现的, 其实这些方法也就是对__dict__的操作进行了封装
需求:要实现一个用于处理用户的终端指令的小框架
框架就是已经实现了最基础的构架,就是所有项目都一样的部分
"""

import plugins

# 框架已经实现的部分
def run(plugin):
while True:
cmd = input("请输入指令:")
if cmd == "exit":
break
# 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
# 判断对象是否具备处理这个指令的方法
if hasattr(plugin,cmd):
# 取出对应方法方法
func = getattr(plugin,cmd)
func() # 执行方法处理指令
else:
print("该指令不受支持...")
print("see you la la!")


# 创建一个插件对象 调用框架来使用它
# wincmd = plugins.WinCMD()
# 框架之外的部分就有自定义对象来完成
linux = plugins.LinuxCMD()
run(linux)
```

插件部分:

```python
class WinCMD:

def cd(self):
print("wincmd 切换目录....")

def delete(self):
print("wincmd 要不要删库跑路?")

def dir(self):
print("wincmd 列出所有文件....")

class LinuxCMD:

def cd(self):
print("Linuxcmd 切换目录....")

def rm(self):
print("Linuxcmd 要不要删库跑路?")

def ls(self):
print("Linuxcmd 列出所有文件....")

```

上述框架代码中 写死了必须使用某个类,这是不合理的,因为无法提前知道对方的类在什么地方 以及类叫什么

所以我们应该为框架的使用者提供一个配置文件,要求对方将累的信息写入配置文件

然后框架自己去加载需要的模块

最后的框架代码:

```python
import importlib
import settings

# 框架已经实现的部分
def run(plugin):
while True:
cmd = input("请输入指令:")
if cmd == "exit":
break
# 因为无法确定框架使用者是否传入正确的对象所以需要使用反射来检测
# 判断对象是否具备处理这个指令的方法
if hasattr(plugin,cmd):
# 取出对应方法方法
func = getattr(plugin,cmd)
func() # 执行方法处理指令
else:
print("该指令不受支持...")
print("see you la la!")


# 创建一个插件对象 调用框架来使用它
# wincmd = plugins.WinCMD()
# 框架之外的部分就有自定义对象来完成

# 框架 得根据配置文件拿到需要的类

path = settings.CLASS_PATH
# 从配置中单独拿出来 模块路径和 类名称
module_path,class_name = path.rsplit(".",1)
#拿到模块
mk = importlib.import_module(module_path)
# 拿到类
cls = getattr(mk,class_name)
# 实例化对象
obj = cls()
#调用框架
run(obj)
```

如此一来,框架就与实现代码彻底解耦了,只剩下配置文件


### 调用之--点调用原理__getattr____setattr____delattr__
点调用的原理
```
#__getattr__,__setattr__,__delattr__
obj点的方式去操作属性时触发的方法
__getattr__: obj.属性 属性不存在时触发
__setattr__: obj.属性=属性的值 设置属性时触发
__delattr__: del obj.属性 删除属性时触发
```
```python
class A:
def __getattr__(self, item):
print("__getattr__")
return 1

def __setattr__(self, key, value):
# print(key)
# print(value)
print("__setattr__")
self.__dict__[key] = value

def __delattr__(self, item):
print("__delattr__")
print(item)
self.__dict__.pop(item)
pass

# def __getattribute__(self, item):
# print("__getattribute__")
# # return self.__dict__[item]
# return super().__getattribute__(item)

a = A()
a.name #触发__getattr__
print(a.name)
a.name = "jack" #触发__setattr__

del a.name
print(a.name) #触发__delattr__
print(a.__dict__)
```

### 点调用之__getattribute__

```python
class A:
def __init__(self,name):
self.name=name
def __getattr__(self, item):
print("__getattr__")
return 1
def __getattribute__(self, item):
print("__getattribute__")
# return self.__dict__[item]
return super().__getattribute__(item)
a = A('0')
a.name
#情况1: 如果有__getattribute__这个函数,先走__getattribute__,如果该属性已有值,则取出属性的值,不触发__getattr__了。
#情况2: 先触发__getattribute__,发现对象没有该属性,则触发__getattr__
```
### 调用之--[]调用原理__getitem____setitem____delitem__
```python

class A:
def __getitem__(self, item):
print("__getitem__")
return self.__dict__[item]

def __setitem__(self, key, value):
print("__setitem__")
self.__dict__[key] = value

def __delitem__(self, key):
del self.__dict__[key]
print("__delitem__")


a = A()
a["name"] = "jack" #触发__setitem__
print(a["name"]) #触发__getitem__
del a["name"] #触发__delitem__

```

### 运算符重载

当我们在使用某个符号时,python解释器都会为这个符号定义一个含义,同时调用对应的处理函数, 当我们需要自定义对象的比较规则时,就可在子类中覆盖 大于 等于 等一系列方法....

案例:

原本自定义对象无法直接使用大于小于来进行比较 ,我们可自定义运算符来实现,让自定义对象也支持比较运算符

```python
class Student(object):
def __init__(self,name,height,age):
self.name = name
self.height = height
self.age = age

def __gt__(self, other):
# print(self)
# print(other)
# print("__gt__")
return self.height > other.height

def __lt__(self, other):
return self.height < other.height

def __eq__(self, other):
if self.name == other.name and self.age == other.age and self.height == other.height:
return True
return False

stu1 = Student("jack",180,28)
stu2 = Student("jack",180,28)
# print(stu1 < stu2)
print(stu1 == stu2)
```

上述代码中,other指的是另一个参与比较的对象,

大于和小于只要实现一个即可,符号如果不同 解释器会自动交换两个对象的位置

### 迭代器协议

```python
迭代器是指具有__iter__和__next__的对象
我们可以为对象增加这两个方法来让对象变成一个迭代器
```

案例:

```python
class MyRange:

def __init__(self,start,end,step):
self.start = start
self.end = end
self.step = step

def __iter__(self):
return self

def __next__(self):
a = self.start
self.start += self.step
if a < self.end:
return a
else:
raise StopIteration

for i in MyRange(1,10,2):
print(i)

```

### 继承里的包装 与 组合方式实现授权 对比
继承里的包装:自己定义的方法与父类同名,调用时用的自定制方法,其余方法用的原来类的方法
```python
class List(list):
#List继承自标准类型list

#重写append
def append(self, p_object):
#若是str则用继承来的append
if type(p_object) is str:
# self.append(p_object)
super().append(p_object)
#若不是str,执行自定义内容
else:
print('只能添加字符串类型')
#自定义一个函数:显示 列表实例 的中间值
def show_midlle(self):
mid_index=int(len(self)/2)
return self[mid_index]

# l2=list('hell oworld')
# print(l2,type(l2)) >>>得到一串 字符串形式的字母 列表

l1=List('helloworld')
# print(l1,type(l1)) >>>type是继承来的方法
# print(l1.show_midlle()) >>>show_midlle是自定义方法
l1.append(1111111111111111111111) #本来是有append方法,但被覆盖重写
l1.append('SB')
print(l1)
```

而授权:也是一种包装,但不是通过继承实现的,是通过 双下__getattr__
```python
import time
class FileHandle:
def __init__(self,filename,mode='r',encoding='utf-8'):
# self.filename=filename
self.file=open(filename,mode,encoding=encoding)
self.mode=mode
self.encoding=encoding
def write(self,line):
print('------------>',line)
t=time.strftime('%Y-%m-%d %X')
self.file.write('%s %s' %(t,line))

def __getattr__(self, item):
# print(item,type(item))
# self.file.read
return getattr(self.file,item) #调read时触发,变成getattr(self.file,read)
#因为self.file在__init__初始化成文件句柄,里面是有read方法,即触发文件里的read

f1=FileHandle('a.txt','w+')
# print(f1.file)
# print(f1.__dict__)
# print('==>',f1.read) #read在FileHandle中找不到,即触发__getattr__
# print(f1.write) #write有定义,使用类已定义的write方法
f1.write('1111111111111111\n')
f1.write('cpu负载过高\n')
f1.write('内存剩余不足\n')
f1.write('硬盘剩余不足\n')
# f1.seek(0)
# print('--->',f1.read())
```

```
#__getitem__,__setitem_,__delitem__
obj[‘属性’]的方式去操作属性时触发的方法

__getitem__:obj['属性'] 时触发
__setitem__:obj['属性']=属性的值 时触发
__delitem__:del obj['属性'] 时触发

#__get__,__set__,__delete__
描述符是一个新式类,这个类至少要实现上述三个方法的一个
class 描述符:
def __get__():
pass
def __set__():
pass
def __delete__():
pass

class 类:
name=描述符()

obj=类()
obj.name #get
obj.name='egon' #set
del obj.name #delete

#__del__:析构方法
垃圾回收时自动触发
```

### 双下方法:
内置的类方法 和 内置的函数之间有着千丝万缕的联系
打印实例时触发__str__,析构方法:程序结束,释放对象 或 删除对象时触发__del__
```python
#转成字符串
#obj.__str__ = str(obj)

class Person:

def __init__(self,name,age):
self.name = name
self.age = age

def __str__(self):
return "这是一个person对象 name:%s age:%s" % (self.name,self.age)

def __del__(self):
print("del run")

p = Person("jack",20)
print(p) #对实例 进行打印时,__str__下返回的值会被打印出来

del p #在对象被del或者程序结束,对象被释放时 触发__del__
print(p)
```

双下__repr__

```python
repr:
print(1) --->1
print('1') --->'1'
obj.__repr__ = repr(obj)
```

双下__len__
```python
obj.__len__ = repr(len)
```

双下__call__
```python
较重要的:__call__

class A:
def __init__(self, name):
pass
def __call__(self):
print('1111')
a=A('alex')
a()
#类中若定义了__call__,那么该类的 对象+() 即会执行__call__方法
#类中若定义了__call__,那么该类生成的实例加括号时,即执行__call__方法
#还有元类中有__call__,那么新类在进行实例化时,即执行元类的__call__
```
其余在元类篇详解

双下__slots__: 有了此限制后,可优化对象的内存空间,并限制对象属性,且对象的__dict__没有了

原理:pyhon 动态语言,可在运行期间动态修改对象属性 要存储更多属性 需要开启更大内存区域 将原始的属性赋值过去,如果开启容量太大 造成内存浪费
解决方案:告诉系统需要几个属性区域
```python
import sys
class Person:
__slots__ = ["name"] #加完此句,p所占内存字节将变小,且再无__dict__
def __init__(self,name):
self.name = name
p = Person("jck")
print(p.name) #虽然没有__dict__,但仍然可以访问到name
print(sys.getsizeof(p)) #内存占用字节
# print(p.__dict__) # dict 没有了
```


### 析构方法__del__的利用
析构方法__del__:程序结束,释放对象 或 删除对象时触发
故可在实例删除时,进行其他的操作,例如文件的关闭,网络端口的关闭
```python
class FileTool:
"""该类用于简化文件的读写操作 """

def __init__(self,path):
self.file = open(path,"rt",encoding="utf-8")
self.a = 100

def read(self):
return self.file.read()

# 在这里可以确定一个事,这个对象肯定不使用了 所以可以放心的关闭问文件了
def __del__(self):
self.file.close()


tool = FileTool("a.txt")
print(tool.read())
```

### 构造方法__new__及单例模式
```python

#__init__初始化方法
#__new__ 构造方法:创建一个对象,init之前就有self,而self是由__new__造出来的
class A:
def __init__(self):
self.x = 1
print('in init function')
# 利用object类中的__new__方法造出新对象并return,return出来的就是self
# def __new__(cls, *args, **kwargs): #cls默认参数代表本类,此时还没有self
# print('in new function')
# return object.__new__(A, *args, **kwargs)

#实例出来的是不同的实例(内存地址不同)
a1 = A()
a2 = A()
a3 = A()
print(a1)
print(a2)
print(a3)
print(a.x)

```


```python
#__eq__:__eq__(slef,other) 判断self对象是否等于other对象

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

def __eq__(self, other):
pass

ob1 = A('egg')
ob2 = A('egg')
print(ob1 == ob2)
#>>>False 原因:虽然参数相同但还是两个实例,内存地址不相同
#__eq__的self和other是实例的内存地址
# ‘==’ 默认比较的其实是内存地址

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

def __eq__(self, other):
if self.name == other.name_:
return True
else:
return False
ob1 = A('egg')
ob2 = A('egg')
print(ob1 == ob2)
#>>>Ture
#定义__eq__后,比较的是两个对象的值,而值是相同的,不用的只是对象

```


```python
只要是可hash的,里面都有__hash__方法

class A:
def __init__(self,name,sex):
self.name = name
self.sex = sex
a = A('egon','男')
b = A('egon','nv')
c = A('egon','男')
print(hash(a))
print(hash(b))
print(hash(c))
#默认对对象的内存地址进行hash
> -9223371859710366888
> 177144408945
> -9223371859710366860


class A:
def __init__(self,name,sex):
self.name = name
self.sex = sex
def __hash__(self):
return hash(self.name+self.sex)
a = A('egon','男')
b = A('egon','女')
c = A('egon','男')
print(hash(a))
print(hash(b))
print(hash(c))
#改成对值的hash
> 9213915433563894301
> -546061345136987036
> 9213915433563894301
```

猜你喜欢

转载自www.cnblogs.com/lishuaing/p/11349307.html