文章目录
语言分类
面向机器
机器容易理解的语言,是一些机器指令,代表有汇编语言
面向过程
做一件事情,按步骤实现,第一步做什么,第二步做什么,如果情况A出现怎么处理,情况B处理怎么处理。适用问题规模小,可以按步骤实现,代表C语言
面向对象OOP
5*
是一种认识世界、分析世界的方法论。将万事万物抽象成为各种对象。
适用问题规模大,复杂的情况,代表C++、Java、Python
类class是一个抽象的概念
对象object,实例instance,都是一个实体,是个具体的概念
数据类,对应属性;动作类,操作类,行为,对应方法,类是对数据和操作的封装。
类中方法也是属性
表达式又名解析式,用来构建list,set, dict, 比for循环效率高,使用列表表达式会立即生成新列表,占用大量的内存和cup;生成器的构建方式有生成器表达式,或者在函数中使用yield关键字,生成器是一个惰性对象,不会立即占用大量内存和cup,可以通过for循环和next方法获得里面的元素,生成器是一种迭代器。
面向对象3要素5*
1.封装
第一,将属性和方法封装
第二,将数据和操作适当暴露给客户,对外提供一些接口, 该隐藏的隐藏,暴露的方法有property中的getter,setter方法. 可以用装饰器实现,也可以自己写函数加到property实例中;隐藏的方法有在属性和方法前面加一个下划线或者两个下划线
2.继承
分为单一继承和多继承,父类的属性和方法可以直接继承过来,
尽量多继承,不要在父类上改写,尽量自己写,OCP开闭原则
单一继承
# 父类 超类 基类
# 子类 派生类
# 继承过程也叫派生过程
class Animal:
def __init__(self, name):
self._name = name
def shout(self):
print('{} , shouts'.format(self.__class__.__name__))
# 在父类中,通过实例取得,子类的名字
class Cat(Animal): pass
c = Cat('tutu')
c.shout()
print(c.__class__.__base__) #Animal
print(c.__class__.__bases__) #元组(Animal, )
print(c.__class__.__mro__) #通过属性获得mro,方法解析顺序,元组里面放的是类Cat Animal object
print(Cat.mro()) # 通过方法获得mro, 方法解析顺序,返回列表,同上
print(Animal.__subclasses__()) #Cat
print(int.__subclasses__()) #布尔型
# # object是根基类,没有父类,父类为None
# print(type(Cat)) type类型
# print(type(object)) type类型
多继承(少用)
python使用MRO(method resolution order 方法解析顺序),解决基类搜素顺序的问题。内部通过C3算法实现,采用深度优先原则
多继承 :一个类继承自多个类,称为多继承
缺点:二义性
原则:OCP原则,多用继承,少修改
用途:复用,在子类上实现对基类的增强,实现多肽
3.多态
面向对象编程最灵活的地方,动态绑定,同一个方法在,不同的类型上
表现不同。例如继承自动物类的人类、猫类的操作’吃’方法不同
多肽:继承加覆盖
同一套方法,在不同的子类上表现不同
这就是多肽
python中的面向对象
定义:类名大驼峰(标识符),class关键字,定义后产生类对象,绑定到标识符上。
class ClassName:
语句块
#实例化的真实过程,先调用了类的 obj = MyClass.__new__()
#然后调用了obj.__init__()方法,但是__init__()的返回值只能是None,
#内部做了优化所以,实例化之后返回一个对象,每次实例化都走一次__init__
#实例化与初始化
#先实例化再初始化
#__init__称为初始化,出厂配置,构造器,构造方法,dunder方法,第一参数必须是实例
#类属性与类方法
#类属性与类方法本质上都是类属性
#类方法method,就是函数,可以通过类标识符.函数名,访问这个属性
#点号,就是成员访问符
普通方法,类方法,静态方法
例子:
class Path:
def __init__(self):
self.name = 'tom'
def method(self, m): #普通方法,需要2个参数,类调用时不会注入第一参数,实例调用时,
# 会将实例自身作为第一参数注入。
print('普通方法:', self, m)
@classmethod
def class_method(cls, b): #类方法,无论类调用还是实例调用,都会将类作为第一参数注入
print('class method :', cls, b)
@staticmethod #(用的少)静态方法, 无论类调用还是实例调用,都不会注入第一参数
def static_method(b, a):
print('static method :', b, a)
# 普通方法例子
print(Path.method) #<function Path.method
print(Path().method) #bound method
Path().method(18) #实例调用时,第一参数为实例
Path.method('tom', 18) #类调用时,第一参数可以随便给一个值
#类方法例子
Path.class_method('tom') #第一参数都为类,函数里面只要遇到self,就都是类
Path().class_method('tom') #第一参数都为类
#静态方法
Path.static_method('11', '22') #第一参数都不注入,需要传入两个参数
Path.static_method('11', '22') #第一参数都不注入,需要传入两个参数
类对象与实例对象的_class_ __name__ __dict__
class Person:
height = 170
def __init__(self, name, age):
self.name = name
self.age = age
def getage(self):
print(self.name, __class__.__name__, self.__class__.__name__,type(self).__name__)
# 后3种都可以取到类的__name__
pclass = Person
pinstance = Person('Tom', 18)
# __name__
pclass.__name__ # Person, 只有类可以取到名字,实例如果不定义取不到name
# __class__
pclass.__class__ # type
pinstance.__class__ # Person
# __dict___
pclass.__dict__ # {'height': 170 , '__init__': ,'getage':, '__doc__': }
pinstance.__dict__ # {'name': 'tom', 'age': 18}
print(*Person.__dict__.items(), sep='\n') #查看当前类的所有,__dict__
函数与类中出现+=比较
class Person:
height = 170
def __init__(self, name, age):
self.name = name
self.age = age
p = Person('tom', 12)
p.height += 10 # 不会报错,不会走__init__,只会到类中去找height属性
p.height
def f():
a = 10
def f1():
a += 1
print(a)
return f1
# f()() 报错
# 另一些例子
class Person:
def __init__(self, name, age=18):
self.name = name
self.age = age
def growup(self, i=1):
self.age += i
print(self.age)
def growup1(self, i=1):
i += 1
print('i', i)
a = 1
def growup2(self):
__class__.a += 1
print('a', __class__.a)
b = 2
def growup3(self):
b += 1
p = Person('tom')
p.growup()
p.growup1()
p.growup2()
p.growup3() #会报错
访问控制
私有,公有,protected
-
self.__age = 1 私有属性,只能类中使用_Person.__age, 当前类名加私有属性名,实例不可以访问, 想查看到_Person.__age,需要调用,
实例 .__dict__
-
_self._age = 1 protected保护的属性,名字还是_age, 可以访问和修改
-
self.age = 1 公有属性
-
同样适用于类的方法,分为私用,保护,公有方法,因为
method也是一种属性
继承中的访问控制
# 一下例子用来测试,正常代码不要这么写
class Animal:
__COUNT = 100
HEIGHT = 0
def __init__(self, age, weight, height):
self.__COUNT += 1
self.age = age
self.__weight = weight
self.HEIGHT = height
def eat(self):
print('{} eat'.format(self.__class__.__name__))
def __getweight(self):
print(self.__weight)
@classmethod
def showcount(cls):
print(cls)
print(cls.__dict__)
print(cls.__COUNT)
@classmethod
def __showcount2(cls):
print(cls.__COUNT)
def showcount3(self):
print(self.__COUNT)
class Cat(Animal):
NAME = 'CAT'
__COUNT = 200
# 测试(只是为了测试)
c = Cat(3, 5, 15)
c.eat()
# print(c.__COUNT) #私有属性不可以通过实例直接访问访问
# CAT类的字典中_Cat__COUNT,访问方式_Cat__COUNT
# Animal类的字典中_Animal__COUNT ,访问方式_Animal__COUNT
# 实例的字典中没有这个属性,实例的字典中有_Animal__COUNT,访问方式_Animal__COUNT
print(Cat._Cat__COUNT) # 200
print(Animal._Animal__COUNT) # 100
print(c._Animal__COUNT) # 101
# c.__getweight() 父类的私有方法不可以通过,实例访问
c._Animal__getweight() #在实例字典中找到了_Animal__weight,值为5
实例属性的访问顺序
class C:
def __init__(self):
print('C init ~~~~~')
class A(C):
def __init__(self):
#super().__init__()
print('A init ~~~~')
class B(A):
def __init__(self):
#uper().__init__()
print('B init ~~~~')
# 构造实例对象时,先找B中的__new__,再找
# A中的__new__, 都没找到,最后找的是object
# 中的__new__; 出厂配置时,初始化的顺序B,A,C
# 如果B中有则只初始化B,如果AC中都有只初始化A
# 如果只有C中有,则只初始化C, 如果想都初始化
# 则需要加2个super
b = B()
# 使用super()加载类中的方法,然后在子类中增强
# 继承的正常使用方法1
class Animal:
def __init__(self, age):
self.age = age
def getage(self):
print(self.age)
class Cat(Animal):
def __init__(self, age, weight):
super().__init__(age)
self.weight = weight
c = Cat(1, 10)
c.getage() # 1
# 继承的正常使用方法2(getage函数的位置改变了)
class Animal:
def __init__(self, age):
self.age = age
class Cat(Animal):
def __init__(self, age, weight):
super().__init__(age)
self.weight = weight
def getage(self):
print(self.age)
c = Cat(1, 10)
c.getage() # 1
以下代码只是为了测试
class Animal:
__COUNT = 100
HEIGHT = 0
def __init__(self, age, weight, height):
self.__COUNT += 1
self.age = age
self.__weight = weight
self.HEIGHT = height
def eat(self):
print('{} eat'.format(self.__class__.__name__))
def __getweight(self):
print(self.__weight)
@classmethod
def showcount(cls):
print(cls)
print(cls.__dict__)
print(cls.__COUNT)
@classmethod
def __showcount2(cls):
print(cls.__COUNT)
def showcount3(self):
print(self.__COUNT)
class Cat(Animal):
NAME = 'CAT'
__COUNT = 200
#
c = Cat(3, 5, 15)
c.eat()
# print(c.__COUNT) #私有属性不可以通过实例直接访问访问
# CAT类的字典中_Cat__COUNT,访问方式_Cat__COUNT
# Animal类的字典中_Animal__COUNT ,访问方式_Animal__COUNT
# 实例的字典中没有这个属性,实例的字典中有_Animal__COUNT,访问方式_Animal__COUNT
print(Cat._Cat__COUNT) # 200
print(Animal._Animal__COUNT) # 100
print(c._Animal__COUNT) # 101
# c.__getweight() 父类的私有方法不可以通过,实例访问
c._Animal__getweight() #在实例字典中找到了_Animal__weight,值为5
super用法
#继承的测试例子(正常代码不要这么写):
class Animal:
def __init__(self, age):
self.age = age
class Cat(Animal):
def __init__(self, age, weight):
super().__init__(age)
self.weight = weight
def getage(self):
print(self.age)
c = Cat(5, 10)
c.getage() # 5
class Animal:
def __init__(self, age):
self.__age = age
def getage(self):
print(self.__age)
class Cat(Animal):
def __init__(self, age, weight):
super().__init__(age) # 等价于super(Cat, self).__init__(age)
# 执行完super,在c实例的字典中多了一个_Animal__age : 3
print(super()) # 打印当前类,和当前类对象,所以super接受的参数也是这两个
self.__age = age + 1 # 执行完这一句,在c对象的字典中多了一个_Cat__age:4
self.weight = weight
c = Cat(3, 10)
c.getage() # Cat类中没有getage方法,所以到父类中找getage方法,找的是c实例中的_Animal__age方法,所以返回3
class Animal:
def __init__(self, age):
self.__age = age
class Cat(Animal):
def __init__(self, age, weight):
super().__init__(age)
self.__age = age + 1
self.weight = weight
def getage(self):
print(self.__age)
c = Cat(3, 10)
c.getage() # 4
class A:
def __init__(self, a, d=10):
self.a = a
self.__d = d
def showd(self):
return self.__d
class B(A):
def __init__(self, b, c):
self.b = b
self.c = c
A.__init__(self, b+c, b-c)
#super(C, f).__init__(2)
def printvalues(self):
print(self.b)
print(self.a)
return self.showd()
b = B(2, 3)
print(b.printvalues()) # b=2, a=5 _A__d=-1
print(b.showd()) # -1
class Animal:
def __init__(self, name):
self._name = name
@property
def name(self):
return self._name #不可以用return self.name, 会形成递归
def shout(self):
print('{} shouts'.format(self.__class__.__name__))
class Cat(Animal):
def __init__(self, name):
super().__init__(name)
def shout(self): #override
# super() #等价于super(self.__class__, self) super(Cat, self)
super().shout()
# 等价于super(Cat, self).shout()
# Animal.shout(self) 不推荐
# self.__class__.__base__.shout(self) #坚决不用,认识就可以
print('miao miao')
c = Cat('tutu')
c.shout() # 'Cat shout'
print(c.name) # tutu
属性装饰器
# 注意事项
class People:
def __init__(self, name):
self._name = name
# @property # 会形成递归,因为加上property后,name变成self的属性,name(self)等价于self.name
# def name(self):
# return self.name
# @property # 初始化时会先执行self.name = name,在报递归错误之前,会报cannot set attribute 错误
# def name(self):
# return self.name
@property
def name(self):
return self._name
p = People('name: tutu')
print(p.name)
# 属性装饰器2个例子
class Person:
def __init__(self):
self.__eyecolor = 'blue'
@property
def eyecolor(self):
return self.__eyecolor
@eyecolor.setter
def eyecolor(self, value):
self.__eyecolor = value
@eyecolor.deleter # 用的不多
def eyecolor(self):
del self.__eyecolor
p = Person()
print(p.__dict__['_Person__eyecolor'])
#p.__eyecolor #访问不到
print(p.eyecolor) #eyecolor函数可以,当作属性来用
p.eyecolor = 'yellow' #修改属性,如果这个属性是只度模式,则不用写下面的settern和deleter方法
del p.eyecolor
# 与上面的方法类似
class Person:
def __init__(self):
self.__eyecolor = 'blue'
def geteyecolor(self): return self.__eyecolor
def seteyecolor(self, value): self.__eyecolor = value
def deleyecolor(self): del self.__eyecolor
eyecolor = property(geteyecolor, seteyecolor, deleyecolor, "I'm the 'eyecolor' property.")
p = Person()
print(p.geteyecolor()) #需要加括号
p.seteyecolor('yellow')
print(p.geteyecolor())
魔术方法5*
容器相关方法
# 将购物出改装成容器练习
# 也可以通过继承的方式将购物出改装成容器
class Item:
def __init__(self, name, price):
self.name = name
self.price = price
def __str__(self):
return '名称:{},价格:{}'.format(self.name, self.price)
__repr__ = __str__
class Cart:
def __init__(self):
self.items = []
def additem(self, item):
self.items.append(item)
return self
def __len__(self):
return len(self.items)
def __str__(self):
return str(self.items)
def __iter__(self):
yield from self.items
# 等价于,return iter(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, key, value):
self.items[key] = value
__add__ = additem
# __repr__ = __str__
c = Cart()
# 大小
print(len(c))
#__bool__
#调用函数bool(), 返回布尔值,没有定义__bool__,就找__len__返回长度,非0为真
#如果__len__也没有定义,那么所有实例都返回真值
class A: pass
a = A()
print(bool(a)) #True
print(bool(A)) #True
class A:
def __len__(self):
return 0
a = A()
print(bool(a)) #False
print(bool(A)) #True 只对实例起作用,对类A,不起作用
# add及可视化
c.additem(Item('car', 200)).additem(Item('earphone', 10))
print(c) #如果类Cart中没有__str__,则返回一个object,如果类Item中没有__str__
# 则外层的object有包括几个object,类Cart中__str_显示最外层的列表,类Item中__str__
# 显示里层的名称和价格
c + Item('macbook', 300) + Item('phone', 400)
print(c)
# 遍历,返回一个新的迭代器对象
# 因为类中有__iter__方法,所以迭代实例c, 相当于迭代yield from形成的生成器对象
# 因为类Item中有__str__方法,所以可以看到具体的内容
for i in c:
print(i)
print('=' * 10)
# __getitem__,key可以是索引或者键(可哈希)
# 加[]相当与调用实例的__getitem__方法
print(c[0])
# __setitem__
# 相当于调用实例的__setitem__方法
c[0] = 'tutu'
print(c)
#in 成员运算符
#__contains__方法如果没有实现
#就调用__iter__方法,判断目标字符串是否在,迭代器中
print('tutu' in c)
#__missing__字典或子类使用__getitem__(),如果key, 不存在会调用此方法
构造器与析构器
class Person:
def __init__(self, name): #构造器,构造函数,资源申请
self.name = name
def __del__(self): #析构器,构造函数,资源释放
print('del instance', self)
def test():
p = Person('tom')
del p #del x 并不直接调用 x.__del__() --- 前者会将 x 的引用计数减一,而后者仅会在 x 的引用计数变为零时被调用
# 即在函数程序执行过程中引用计数变成0,调用了__del__
print('*' * 10)
test() # 执行完函数,不会打印:del instance <__main__.Person object at 0x10236c588>
# 因为在函数内部类对象的引用计数已经被清0了,会在*好上面打印,起作用的是def
def test():
p = Person('tom')
print('*' * 5)
test() # 执行完函数,才会执行__del__ 在*号之后打印:del instance <__main__.Person object at 0x10236c588>
# p = Person('tom')
# print(p.name) # 执行完最后一句print,才会执行__del__,打印:del instance <__main__.Person object at 0x10236c588>
#实例化 ?????????????????
#__new__是静态方法,所以第一参数必须手动给,这个方法很少用到
# 即使创建了该方法,也会调用一次,super().__new__(cls)
class A:
def __new__(cls, *args, **kwargs):
super().__new__(cls)
def __init__(self, name, age):
self.name = name
self.age = age
a = A('tom', age=20)
print(a.name)
__call__方法
# 在类中定义一个__call__方法,**实例**就可以像函数一样调用
class Adder:
def __call__(self, *args):
self.result = sum(args)
return self.result
# 以下两种调用方法等效
print(Adder()(*range(5)))
a = Adder()
print(a.__call__(*range(5)))
a = Adder()
print(callable(Adder)) #实例与类都是可调用的
print(callable(a))
# 函数调用的本质:
# 正常的函数调用,bin() ,实际是调用了bin.__call__()
__hash__
# 判断一个对象是否可hash, isinstance(p1, collections.Hashable)
# list实例为什么不可以hash?
# 因为List类中有__hash__ = None, 但是list本身是可以hash的,print(hash(list)),
# 所有的类都继承自object, object类中有__hash__(),所以所有的类都是可hash的,
# 如果所有的类不可hash,需要在object中设置
class A:
pass
print(A)
print(A()) #什么都不写,类和实例也都可hash
print('=' * 10)
class A:
__hash__ = None
print(hash(A)) #可hash,
#print(hash(A())) #不可hash,因为None类型不可以这么使用None(A())
# print(A.__hash__()) # 不等价于 hash(A), 因为前面不可调用,但是后面
# 可hash
#集合去重的本质,需要hash值相同,并且内容相同
class A:
def __init__(self, name):
self.name = name
def __hash__(self):
return hash(self.name)
def __eq__(self, other):
return self.name == other.name
def __str__(self):
return str(self.name)
__repr__ = __str__
a = A('tom')
b = A('tom')
print(a is b) #False
print(a == b) #True
print(hash(a)) #5241461326091852353
print(hash(b)) #同上
print({a, b}) #去重了只剩下,tom
a = A('tom')
b = A('jerry')
print(a is b) #False
print(a == b) #False
print(hash(a)) #5241461326091852353
print(hash(b)) #不同上
print({a, b}) #{tom, jerry} 没有去重
class A:
def __init__(self, name):
self.name = name
def __hash__(self):
return 123
def __eq__(self, other):
return self.name == other.name
def __str__(self):
return str(self.name)
__repr__ = __str__
a = A('tom')
b = A('tom')
print(a is b) #False
print(a == b) #True
print(hash(a)) #123
print(hash(b)) #同上
print({a, b}) #去重了只剩下,tom
a = A('tom')
b = A('jerry')
print(a is b) #False
print(a == b) #False
print(hash(a)) #123
print(hash(b)) #同上
print({a, b}) #{tom, jerry} 没有去重
class A:
def __init__(self, name):
self.name = name
def __hash__(self):
return 123
def __str__(self):
return str(self.name)
__repr__ = __str__
a = A('tom')
b = A('tom')
print(a is b) #False
print(a == b) #False 如果类中没写__eq__那么这一行相当于判断is,地址是否相同
print(hash(a)) #123
print(hash(b)) #同上
print({a, b}) #{tom, tom},没有去重复
print(hash(1)) #居然等于1
可视化方法
class Person:
def __init__(self, name):
self.name = name
def __str__(self):
return '<A {} >'.format(self.name)
def __bytes__(self):
return str(self).encode()
__repr__ = __str__
p = Person('tom')
print(p)
print(str(p))
print(repr(p))
print('{}'.format(p))
print(bytes(p)) # b'<A tom >'
运算符重载
# 当类中需要进行大量运算时使用,int类几乎实现了所有操作符,可以作为参考
# 自己是现实只需要实现3中运算符,其他3种可以推断出来
# __eq__ 由等于可以推出不等于
# __ge__ 由大于等于可以推断出小于等于
# __gt__ 由大于可以推断出小于
class A:
def __init__(self, name, age=18):
self.name = name
self.age = age
def __eq__(self, other): #等效于 ==
return self.age == other.age
def __gt__(self, other):
return self.age > other.age
def __ge__(self, other):
return self.age >= other.age
def __add__(self, other):
return self.age + other.age
def __iadd__(self, other):
self.age += other.age
return self.age
def __sub__(self, other):
pass
def __isub__(self, other):
pass
def __str__(self):
return self.name
__repr__ = __str__
a = A('tom')
b = A('jerry', 25)
# 可以直接对两个实例进行排序
# 因为实例中含有__str__方法,所以会显示名字[tom, jerry],默认是升序,实际比较
# 的是实例,名字只是显示
print(sorted([a, b]))
print(a == b)
print(a > b)
print(a <= b)
print(a != b)
# 在类上面加一个装饰器@total_ordering,但是这个装饰器效率不高,一般不用
# from functools import total_ordering
print('=' * 10)
print(a + b)
a += b
print(a)
# 另一个例子
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return self.x + other.x, self.y + other.y
# #方法1
# def __iadd__(self, other):
# self.x, self.y = self + other
# return self
# #方法2
# def __iadd__(self, other):
# return Point(*(self + other))
# #方法3
# def __iadd__(self, other):
# self.x += other.x
# self.y += other.y
# return self
#方法4
def __iadd__(self, other):
self.x = self.x + other.x
self.y = self.y + other.y
return self
def __str__(self):
return str((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(2, 1)
p1 += p2 #为什么一定要有返回值呢???????????????
print(p1)
查看属性的方法__dir__
# 特殊属性
# __name__ 类、函数、方法等的名字,实例没有
# __module__ 类定义所在的模块名字
# __class__ 实例或者类所属的类
# __bases__ 类的基类,返回一个元组,顺序为它们在基类列表中出现的顺序
# __doc__ 类或者函数的文档字符串,如果没有定义则为None
# __mro__ 类的继承顺序,class.mro()返回的结果保存在,__mro__中
# __dict__ 类或者实例的属性
#查看属性的方法__dir__
#dir()函数操作实例就是调用__dir__()
# dir(obj)
# obj的类型:
# 模块名,返回的**列表**包含模块的属性名和变量名
# 例如:print(dir('OOP魔术方法练习.py'))
# 类或者类型,返回的列表包含类的属性名,及它祖先的属性名
# 类的实例,
# 有__dir__方法,返回可迭代对象的返回值
# 没有__dir__方法,返回列表包含,实例的属性名,类的属性名,祖先类的属性名
# dir() #里面什么都不写
# 在模块中,返回模块的属性和变量名
# 在方法或者函数中,返回本地作用域的变量名
# 内建函数:
print(locals()) # 返回当前作用域中,变量**字典**
print(globals()) # 返回当前模块全局变量的字典
方法重载(overload)
函数名字相同,但是参数的类型不同, 其他语言可以使用,但是python的重载,如下面的例子所示, 同一个add函数可以实现,整数相加,字符串相加,也可以接受多个参数, 即灵活的形参,python语法本身就实现了其他语言的重载
class Add:
def add(self, a, b):
return a + b
Add().add(1, 2)
Add().add('a', 'b')
override 重写,覆盖
猴子补丁(发现python的方法或类写的不好,用的不多)
from test2 import Person
from test3 import get_score
def monkeypatch4Person():
Person.get_score = get_score
monkeypatch4Person()
# 修改了Person,中的get_score方法
增加子类功能的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) 收集对象的属性