重要思想:python中万物皆对象!
1、函数重写(有了的前提下改写)
- 重写:override(v. (以权力)否决,推翻)在继承的前提下,如果在子类中重新实现了父类中的函数,才能进行函数重写。
- 自定义函数的重写:
- 1.什么时候需要重写函数
如果一个类有很多的子类,一部分子类可以直接继承父类中的函数实现的功能。
但是,如果父类中实现的需求满足不了个别子类的使用,则需要在子类中重写父类中的函数。
- 2.重写需要注意的事项
保留函数的声明部分【def xxxx(形参列表)】
重新实现函数的实现部分【函数体】
#函数重写
#1、函数重写(自定义函数)
#1.1、如果多个父类中出现了重名的函数,当子类对象调用的时候,默认情况下,调用的是父类列表中第一个父类中的函数
class P(object):
def __init__(self,id_P):
self.id_P = id_P
def f1(self):
print('父类P~~~~~~')
class B(object):
def __init__(self,id_B):
self.id_B = id_B
def f1(self):
print('父类B~~~~~~')
class PB(P,B):
pass
pb = PB(1)
pb.f1() #父类P~~~~~~
#1.2、完全重写:在子类中实现自己的功能
class P1(P):
def f1(self):
print('子类P1~~~~~~')
p1 = P1(1)
p1.f1() #子类P1~~~~~~
#1.3、在需要使用父类功能的基础上,增加自己的功能,则可以调用父类的函数
class P2(P):
def f1(self):
P.f1(self)
print('子类P2~~~~~~')
p2 = P2(2)
p2.f1() #父类P~~~~~~ 子类P2~~~~~~
#1.4、重写的好处
class Animal():
def walk_style(self):
print("走路")
class Cat(Animal):
pass
class Dog(Animal):
pass
# 使用父类功能的同时,增加自己的功能
class Bird(Animal):
def walk_style(self):
Animal.walk_style(self)
print("飞行")
# 完全重写
class Fish(Animal):
def walk_style(self):
print("游走")
cat = Cat()
cat.walk_style() #走路
bird = Bird()
bird.walk_style() #走路 飞行
fish = Fish()
fish.walk_style() #游走
#2、函数重写(系统函数)
"""
__str__:返回对象的字符串描述信息,面向用户
__repr__:返回对象的字符串描述信息,面向程序员
"""
#2.1、当打印对象的时候,自动调用object类中的__str__函数,该函数默认返回当前对象在内存空间中的地址
class Person1(object):
def __init__(self,name,age):
self.name = name
self.age = age
p1 = Person1('小张',18)
print(p1) #<__main__.Person1 object at 0x0000018AAA3F5A30>
#重写__str__
class Person2(object):
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return f"姓名:{self.name},年龄:{self.age}" #返回一串格式化的字符串第一次见
p2 = Person2('小张',18)
print(p2) #姓名:小张,年龄:18
print(p2.__str__()) #姓名:小张,年龄:18
p21 = Person2('小李',18)
p22 = Person2('小吴',20)
#2.2、当只重写了__str__,打印对象的时候可以显示指定内容,但是如果将对象作为列表的元素,打印列表名,显示的仍然是地址
l = [p2,p21,p22]
print(l) #[<__main__.Person2 object at 0x0000023CC02FC730>, <__main__.Person2 object at 0x0000023CC02FC6A0>, <__main__.Person2 object at 0x0000023CC02FB070>]
for i in l:
print(i) #姓名:小张,年龄:18 姓名:小李,年龄:18 姓名:小吴,年龄:20
#这里要想print(l)也打印列表信息,就需要对__reper__重写
class Person3(object):
def __init__(self,name,age):
self.name = name
self.age = age
def __str__(self):
return f"姓名:{self.name},年龄:{self.age}" #返回一串格式化的字符串第一次见
__repr__ = __str__ #函数的本质是变量(函数名指向那函数就是什么)
p3 = Person3('小张',18)
l1= [p3]
print(l1) #[姓名:小张,年龄:18]
#3、为什么Python中int,float,str,list...等在打印的时候不是打印地址,而是打印它里面的值
n = 1
print(n) #1
#说明:对于Python中常用的数据类型【类】,如:int,float,str,list....都已经重写了__str__
2、运算符重载(不支持变为支持)
函数重写(override) | 必须是在继承的前提下 |
运算符重载(overload) | 对于自定义的类,通过该类创建的对象,不支持某些运算符的使用,则可以重载对应的函数 |
- 通过dir函数可以查看对应的数据类型支持那些方法(包括运算符)
- 如: +:add *:mul 等算术运算符
- 如:>:gt <:lt ==:eq !=:ne >=:ge <=:__le__等关系运算符
#1、运算符重载
#1.1、运算符的意义
print(34 + 45) #79
print('hello' + 'Python') #helloPython 拼接
print("hello".__add__("Python")) #helloPython 拼接
print([1,2,3] + [3,5,6]) #[1, 2, 3, 3, 5, 6] 组合
print([1,2,3].__add__([3,5,6]))#[1, 2, 3, 3, 5, 6] 组合
# print({'a':10} + {'b':20}) #TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
# dir(x):可以查看指定对象x的相关的信息
# print(dir('a'))
# print(dir([34]))
# print(dir((45,5)))
# print(dir({'a':10}))
#1.2、在类中重载运算符的意义
class Person():
def __init__(self,height):
self.height = height
def __str__(self):
return str(self.height)
__repr__ = __str__
p1 = Person(180)
p2 = Person(163)
# print(dir(p1)) #可以看到在对象p1当中没有__add__
# print(p1 + p2) #TypeError: unsupported operand type(s) for +: 'Person' and 'Person'
# print(p1.__add__(p2)) #TypeError: unsupported operand type(s) for +: 'Person' and 'Person'
'''#重载__add__
注意:结合实际情况,一般两个对象相加是没法相加的,都是对象的属性相加
需求:两个Person的对象相加,实际操作的是身高
问题1:Person + Person ----->int
正确:Person + Person ----->Person
解决方案:return self.height + other.height ------> return Person(self.height + other.height)
问题2:打印p1 + p2结果是一个对象的地址
解决方案:重写__str__,返回str(self.height)
'''
class Person1():
def __init__(self,height):
self.height = height
def __str__(self):
return str(self.height)
__repr__ = __str__
# 当前类的对象不支持+,所以在类中重载__add__
def __add__(self, other):
return Person(self.height + other.height) #保持“对象+对象=对象”的格式
p11 = Person1(180)
p12 = Person1(163)
# print(dir(p11)) #可以看到在对象p11当中重载了__add__
print(p11 + p12) #343 (在重载之后虽然返回的是对象,但是调用了__str__返回的就不是地址了)
print(p11.__add__(p12)) #343
#1.3、重载运算符的其他例子
"""
>:__gt__ greater than
<:__lt__ less than
==:__eq__ equal
!=:__ne__ not equal
>=:__ge__ greater equal
<=:__le__ less equal
"""
class Person2():
def __init__(self,height):
self.height = height
# 在实际需求中,不会直接比较对象,都是比较对象的属性
def __gt__(self, other):
return self.height > other.height
p21 = Person2(180)
p22 = Person2(163)
print(p21 > p22) #True
print(p21.__gt__(p22)) #True
3、类方法和静态方法
- 注意:现在学完了类之后,以后写类需要有的一些必备元素:
#3、类方法和静态方法以及实例函数
#3.1、演示一个完整的类的写法
class Full(object):
#限制对象属性
__slots__ = ('name','age')
#类属性
n = 0
#构造函数
def __init__(self,name,age):
self.name = name
self.age = age
#类函数
@classmethod
def f1(cls):
print('这是类函数~~~')
#实例(对象)函数
def f2(self):
print('这是实例函数~~~')
#静态函数
@staticmethod
def f3():
print('这是静态函数~~~')
def __str__(self): #重写系统函数
return self.name
__repr__ = __str__ #重写系统函数
#定义对象
p_t = Full('张',15)
#用对象调用“类函数、实例函数、静态函数”
p_t.f1() #这是类函数~~~
p_t.f2() #这是实例函数~~~
p_t.f3() #这是静态函数~~~
#用类名调用“类函数、实例函数、静态函数”
Full.f1() #这是类函数~~~
# Full.f2() #TypeError: f2() missing 1 required positional argument: 'self'
Full.f3() #这是静态函数~~~
'''
总结:
【面试题】简述实例函数,类函数和静态函数之间的区别和联系
不同点:
a.是否有装饰器装饰:实例函数无需装饰器装饰,类函数需要使用@classmethod装饰,静态函数需要使用@staticmethod装饰
b.形参不同:实例函数的第一个参数为self,类函数的第一个参数为cls,静态函数的参数没有要求
c.调用不同:类函数和静态函数都可以通过对象 或者 类 调用,但是,实例函数只能通过对象调用 (对象可以调所有,类不能调实例函数)
d.使用场景不同:如果要封装一个工具类,对参数没有要求,则尽量使用静态函数,如果有要求,则使用类函数
在实际项目开发中,使用实例函数较多。
相同点(他们本身都是函数):
a.可以使用默认参数,关键字参数,不定长参数
b.可以使用返回值
'''
# 练习:定义一个工具类,可以实现两个数加减乘除的运算
class Math(object):
@staticmethod
def add(num1,num2):
return num1 + num2
@staticmethod
def sub(num1, num2):
return num1 - num2
@staticmethod
def mul(num1, num2):
return num1 * num2
@staticmethod
def div(num1, num2):
return num1 / num2
print(Math.add(34,5)) #39
print(Math.sub(34,5)) #29
print(Math.mul(34,5)) #170
print(Math.div(34,5)) #6.8
4、对象的其他概念
- 对象的内置属性:可以理解为对象的系统属性
- 注意:内置属性__slots__作为限制,有时候会导致对象无法调用内置属性
- 对象的内置函数:可以理解为对象的系统函数
# 4、对象的其他概念(对象内置属性、对象内置函数)
# 4.1、对象内置属性:
# 4.1.1、__slots__:限制对象属性的动态绑定 ****
class Person():
__slots__ = ("name",)
# 4.1.2、__doc__:表示类的描述信息,获取类中的文档注释
class Check(object):
"""
功能:实现数据的校验
"""
def show(self):
pass
print(Check.__doc__)
'''
打印结果:
功能:实现数据的校验
'''
# 4.1.3、__dict__:获取类或对象的信息,包括属性和方法 ******
class Pe(object):
# 类属性
place = "地球"
# 限制属性
# __slots__ = ("name",) #如果在这限制了对象属性,则后面对象调用__dict__就会报错:AttributeError: 'Pe' object has no attribute '__dict__'
# 构造函数
def __init__(self, name):
# 实例属性
self.name = name
# 实例函数
def show(self):
pass
# 类函数
@classmethod
def func1(cls):
pass
# 静态函数
@staticmethod
def func2():
pass
def __str__(self):
return self.name
__repr__ = __str__
print(Pe.__dict__) # 获取类的信息,包括属性和方法
a = Pe('f')
print(a.__dict__) # {'name': 'f'}
'''
总结:
a.对象.__dict__只能获取对象(实例)属性
b.类名.__dict__可以获取类中的类属性,结构函数,实例函数,类函数和静态函数
c.__dict__获取的数据是通过字典的形式展现
'''
# 4.1.4、__module__:获取当前对象在哪个模块,类似于__name__(区别是他和__name__的调用方法不一样)
p = Pe('aaa')
print(p.__module__) # __main__
print(__name__) # __main__
# 4.1.4、__name__ *******
'''
功能:
a.在函数中“if __name__ == '__main__':”相当于主函数入口。
b.用来做工具模块的测试,只有在工具模块中运行,在其他文件中调用不执行。
c.__name__属性特殊之处在于可以直接调用
'''
# 4.2、对象内置函数:
# 1.id():获取一个对象在内存中的地址
# 比较两个对象的地址是否相同
a = 45
b = 46
print(id(a) == id(b)) # False
print(a is b) # False
# 2.type():获取一个对象的数据类型
print(type("fajgh")) # <class 'str'>
# 比较变量的类型 *****
print(type("abc") == str) # True
print(type("123") == type(123)) # False
print(type(type(123))) # <class 'type'>
# 练习:提取一个列表中所有字符串类型
l11 = [1, 2, 3, 4, 'dd', '33', 55]
l12 = [i for i in l11 if type(i) == str]
print(l12) #['dd', '33']
# 自定义类的类型,格式:<class '__main__.类名'>
class Per_son(object):
pass
p = Per_son()
print(type(p)) #<class '__main__.Per_son'>
# 借助于types模块,可以判断函数的类型
import types
print(type(lambda x:x ** 2) == types.LambdaType) #True
print(type((i for i in range(5))) == types.GeneratorType) #True
# 3.isinstance():判断一个对象是否是某种指定的数据类型 ******
print(type("abc") == str) #True
print(isinstance("abc",str)) #True
# 4.dir() :查看一个对象的信息 ******
print(dir(34)) #['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
# 5.issubclass(类1,类2):判断两个类之间是否具有继承关系(类1是否继承自类2)
class Animal(object):
def __init__(self,name):
self.name = name
def eat(self):
print("eating")
class Cat(Animal):
pass
class LittleCat(Cat):
pass
print(issubclass(Cat,Animal)) #True
print(issubclass(Cat,object)) #True
print(issubclass(LittleCat,Animal)) #True
print(issubclass(Animal,Cat)) #False