重要思想: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