一、运算符重载【重点掌握】

"""
【面试题】简述函数重写和函数重载的区别
函数重写:override,在继承的前提下,如果子类中重新实现了父类中的函数
运算符重载/函数重载:overload,对于自定义的类,通过该类创建的对象,如果不支持某些运算,
                 如:+ - * / > <  >=  <= ==  !=等,则可以在该类中实现这些运算符对应的函数
"""

# 系统中定义的函数,如果命名的前后各两个下划线,则该类函数被称为魔术函数/魔术方法/双下划线方法

# 1.dir(对象):获取指定对象相关的函数或属性
# print(dir("abc"))
# print(dir(10))
# print(dir([1,3]))
# print(dir((1,3)))
# print(dir({'a':10}))

# 2.+ - *  /
"""
__add__()   +
__sub__()   - 
__mul__()   *
__mod__()   %
__divmod__  /
"""
# __add__()为例
# a
print(12 + 10)
print(12 + 45.6)
print(34.5 + 9.7)

print('abc' + 'def')
print('abc'.__add__("def"))

print([1,2] + [4,6])
print([1,2].__add__([4,6]))

print((1,2) + (3,6))
# print({'a':10} + {'b':20})

print("*" * 50)

# b.
class Person():
    def __init__(self,age):
        self.age = age
    # 重载
    def __add__(self, other):
        # self和other表示参与运算的两个对象
        # 结合实际情况,两个对象无法直接相加,可以对两个对象的属性进行加法运算
        return Person(self.age + other.age)

    # 重写
    def __str__(self):
        return str(self.age)
    __repr__ = __str__

p1 = Person(10)
p2 = Person(5)
# 问题:Person + Person = int---->Person + Person = Person
print(p1 + p2)
print(p1.__add__(p2))

"""
工作原理:
    p1 + p2----》p1.__add__(p2)----》Person(self.age + other.age)
    print(p1 + p2)----》print(Person(self.age + other.age))---》自动调用__str__
"""

# 3.  > <  >=  <= ==  !=
"""
__eq__()    equal,==
__ge__()    greater equal  >= 
__gt__()    greater  than   >
__le__()    less  equal   <= 
__lt__()    less than   <
__ne__()    not equal   !=
"""
class Person():
    def __init__(self,age):
        self.age = age

    # 自定义比较规则
    def __gt__(self, other):
        # self和other是参与比较的两个对象
        # 一般情况下,结合实际的使用,参与比较的是对象的同类属性
        # if self.age > other.age:
        #     return True
        # return False

        # return True if self.age > other.age else False

        return self.age > other.age

p1 = Person(10)
p2 = Person(5)
print(p1 > p2)   # True

二、多态

一种事物的多种体现形式,举例:动物有很多种

定义时并不确定是什么类型,要调用的是哪个方法,只有运行的时候才能确定调用的是哪个

注意:继承是多态的前提

# 多态:必须是在继承的前提下

# 1.一种事物的多种体现形式【一个对象的多种数据类型】
class Animal(object):
    pass
class Cat(Animal):
    pass
class SmallCat(Cat):
    pass

c = SmallCat()
# isinstance(对象,类型):查看某个对象是否是指定的数据类型
print(isinstance(c,SmallCat))
print(isinstance(c,Cat))
print(isinstance(c,Animal))
print(isinstance(c,object))

a = Animal()
print(isinstance(a,Animal))
print(isinstance(a,Cat))

# 2.定义时并不确定是什么类型,要调用的是哪个方法,只有运行的时候才能确定调用的是哪个
class Animal(object):
    def show(self):
        print("Animal~~~~~~~show")
class Cat(Animal):
    def show(self):
        print("Cat~~~~~~~show")
class SmallCat(Cat):
    def show(self):
        print("SmallCat~~~~~~~show")

def func(a):
    a.show()

c = SmallCat()
func(c)

c1 = Cat()
func(c1)

三、其他

1.对象的内置属性

# 1.__slots__:限制对象属性的动态绑定                   ****
class Person():
    __slots__ = ("name",)

# 2.__doc__:表示类的描述信息,获取类中的文档注释
class Check(object):
    """
    功能:实现数据的校验
    """
    def show(self):
        pass
print(Check.__doc__)

# 3.__dict__:获取类或对象的信息,包括属性和方法               ******
class Person(object):
    # 类属性
    place = "地球"
    # 限制属性
    __slots__ = ("name",)
    # 构造函数
    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__

# 类名.__dict__:可以获取类中类属性,实例属性,构造函数,实例函数。类函数,静态函数
print(Person.__dict__)
print(list.__dict__)   # dir()

# 4.__module__:获取当前对象在哪个模块,类似于__name__
p = Person('aaa')
print(p.__module__)
print(__name__)   # if __name__ == "__main__":

# 5.__class__:类似于type(xxx),获取一个对象的类型
print(p.__class__)
print(type(p))

2.对象的内置函数

# 1.id():获取一个对象在内存中的地址
# 比较两个对象的地址是否相同
a = 45
b = 46
print(id(a) == id(b))
print(a is b)

# 2.type():获取一个对象的数据类型
print(type("fajgh"))

# 比较变量的类型       *****
print(type("abc") == str)
print(type("123") == type(123))
print(type(type(123)))  # <class 'type'>

# 自定义类的类型,格式:<class '__main__.类名'>
class Person(object):
    pass
p = Person()
print(type(p))

# 借助于types模块,可以判断函数的类型
import  types
print(type(lambda x:x ** 2) ==  types.LambdaType)
print(type((i for i in range(5))) == types.GeneratorType)

# 3.isinstance():判断一个对象是否是某种指定的数据类型         ******
print(type("abc") == str)
print(isinstance("abc",str))

# 4.dir() :查看一个对象的信息                      ******
print(dir(34))

# 5.issubclass():判断两个类之间是否具有继承关系
class Animal(object):
    def __init__(self,name):
        self.name = name
    def eat(self):
        print("eating")
class Cat(Animal):
    pass
print(issubclass(Cat,Animal))
print(issubclass(Cat,object))

3.类方法和静态方法【重点掌握】

【面试题】简述实例函数,类函数和静态函数之间的区别

a.定义不同:被@classmethod装饰的函数表示类函数,被@staticmethod装饰的函数表示静态函数,实例函数没有指定的装饰器

b.形参列表不同:实例函数的第一个参数是self,表示当前对象,类函数的第一个参数是cls,表示当前类,静态函数对参数没有要求

c.调用方式不同:类函数和静态函数都可以通过类名或对象调用,但是,实例函数只能通过对象调用

d.使用场景不同:如果要封装一个工具类,则尽量使用类函数或静态函数,在实际项目开发中,实例函数使用较多,如果需要在类中的函数中创建对象,则一般会将该函数定义成类函数

class Person():
    # 类属性
    place = "地球"

    # 构造函数
    def __init__(self,name,age):
        # 实例属性
        self.name = name
        self.age = age

    # 实例函数
    # self:表示当前对象
    def show(self,sth):
        print("showing",sth,self.name,self.age)

    # 类函数
    # cls:class的缩写,表示当前类,使用cls相当于使用Person
    @classmethod
    def func(cls,num1,num2):
        print('func~~~~',num1,num2)

        # 需求:在类函数中调用实例函数
        # 第一步:创建对象
        c = cls(100,200)   # 等价于Person(xx,xx)

        # 第二步:调用
        c.show(465)

    #静态函数
    @staticmethod
    def check(a,b,c):
        print('check~~~',a,b,c)


Person.func(12,45)
# Person.check(3,6,7)
#
# p = Person('aaa',10)
# p.show('apple')
# p.func(34,7)
# p.check(243,6,8)


# 应用:定义一个工具类,其中可以进行两个数的加减乘除的运算
class Math():
    @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,10))

四、单例设计模式【重点掌握】

1.概念

什么是设计模式?

设计模式是经过总结、优化的,对我们经常会碰到的一些编程问题的可重用解决方案

设计模式更为高级,它是一种必须在特定情形下实现的一种方法模板。设计模式不会绑定具体的编程语言

 23种设计模式,其中比较常用的是单例设计模式,工厂设计模式,代理模式,装饰者模式等等

什么是单例设计模式?

单例:单个实例【单个对象】,一个类只能创建一个对象

程序运行过程中,确保某一个类只有一个实例【对象】,不管在哪个模块获取这个类的对象,获取到的都是同一个对象。该类有一个静态方法,向整个工程提供这个实例,例如:一个国家只有一个主席,不管他在哪

单例设计模式的核心:一个类有且仅有一个实例,并且这个实例需要应用于整个程序中,该类被称为单例类

思考:验证两个变量表示的是同一个对象,该怎么验证?

解决:验证地址,方式:is 或者 id()

2.应用场景

应用程序中描述当前使用用户对应的类 ———> 当前用户对于该应用程序的操作而言是唯一的——> 所以一般将该对象设计为单例

实际应用:数据库连接池操作 ——> 应用程序中多处地方连接到数据库 ———> 连接数据库时的连接池只需一个就行,没有必要在每个地方都创建一个新的连接池,这种也是浪费资源 ————> 解决方案也是单例

3.实现

3.1方式一

# 定义一个类,该类只能创建一个对象,则该类被称为单例类,这种实现方式被称为单例设计模式

# 1,普通类:可以创建无数个对象
class Person1(object):
    def __new__(cls, *args, **kwargs):
        print("new被调用了~~~~~")
        return super().__new__(cls)
    def __init__(self,name,age):
        print('init被调用了')
        self.name = name
        self.age = age
p1 = Person1('张三',10)
p2 = Person1("李四",20)
print(p1 is p2)      # False
print(id(p1),id(p2))

print("*" * 50)

# 2
class Person2(object):

    # 定义一个私有类属性,表示当前类可以创建的唯一的实例
    __instance = None

    # super().__new__(cls)但凡执行一次,就会创建一个新的对象,
    # 所以只要super().__new__(cls)从头到尾只执行一次,则表示只有一个对象
    def __new__(cls, *args, **kwargs):
        print("new被调用了~~~~~")

        # 判断__instance是否为None,如果是,则将super().__new__(cls)赋值给__instance
        # 如果不是,则直接返回
        if not cls.__instance:
            print("if语句的执行")
            cls.__instance = super().__new__(cls)
        return cls.__instance

    def __init__(self,name,age):
        print('init被调用了')
        self.name = name
        self.age = age

p1 = Person2('张三',10)    # 创建
p2 = Person2("李四",20)    # 返回第一次创建的对象
print(p1 is p2)       # True
print(id(p1),id(p2))

# 原因:每次执行p = Person2(xx,xx),都会执行__init__()
print(p1.name,p2.name)   # 李四

p1.name = "aaa"
print(p1.name,p2.name)   # aaa

3.2方式二

# 1.装饰器装饰函数
def wrapper(func):
    print('1111')
    def inner():
        print("new~~~~~~")
        func()  # test()
    return  inner
@wrapper    # 调用wrapper()
def test():
    print("testing")
test()     # inner()


print("*" * 50)

# 2.装饰器装饰类
def wrapper(cls):
    print('1111')
    def inner():
        print("new~~~~~~")
        cls()  # Test()创建对象,构造函数
    return  inner
@wrapper    # 调用wrapper(Test)
class Test():
    print("testing")
Test()     # inner()

print("*" * 30)

# 3.
def singleton(cls):   # cls--->Person1
    # 定义instance,表示被装饰的类可以创建的唯一的对象
    instance = None
    def get_instance(*args,**kwargs):
        nonlocal instance
        if not instance:
            instance = cls(*args,**kwargs)  # 创建被装饰的类的对象,调用__init__
        return  instance
    return get_instance

@singleton
class Person1(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
        print('init被调用了',self.name,self.age)

p1 = Person1('张三',10)   # 调用get_instance函数
p2 = Person1("李四",20)
print(p1 is p2)      # True
print(id(p1),id(p2))

# 原因:只有第一次执行p = Person1(xx,xx),才会执行cls(*args,**kwargs),也就会执行__init__()
print(p1.name,p2.name)   # 张三

p1.name = "aaa"
print(p1.name,p2.name)   # aaa

3.3方式三

def singleton(cls):
    # 定义一个字典,用于存储被装饰的类和对应的唯一的对象
    # key:被装饰的类     value:被装饰的类能够创建的唯一的对象
    instance_dict = {}
    def getinstance(*args,**kwargs):
        # 判断instance_dict为空,则向字典中添加一对键值对
        if not instance_dict:
            instance_dict[cls] = cls(*args,**kwargs)
        return instance_dict[cls]
    return getinstance
@singleton
class Person(object):
    def __init__(self,name):
        self.name = name
p1 = Person("张三")
p2 = Person("李四")
print(p1 is p2)
print(id(p1) == id(p2))

print(p1.name,p2.name)
p1.name = "tom"
print(p1.name,p2.name)

五、抽象类

"""
如果说类是从多个对象中抽取相同的属性和方法,则抽象类表示从一堆类中抽取相同的属性或方法
抽象类是一个特殊的类,他的特殊之处在于只能被当做父类使用,也就是说,只能被继承,不能被实例化
本质:抽象类就是为被继承的,其中提供子类可以使用的接口,所以在实际开发中,充当父类
"""

# 在Python中,本身没有抽象类,但是,可以通过abc模块模拟
import abc      # abstract  class

# 抽象类/父类
class AllFile(metaclass=abc.ABCMeta):
    # 在抽象类中定义抽象方法,该方法的作用只是给子类提供一个接口,在子类中只需要实现该接口即可
    @abc.abstractmethod
    def read(self):
        pass
    @abc.abstractmethod
    def write(self):
        pass

    # def show(self):
    #     pass

# 子类
class Txt(AllFile):
    # 重写
    def read(self):
        print("txt文件的读取")
    def write(self):
        print("txt文件的写入")

class Excel(AllFile):
    # 重写
    def read(self):
        print("excel文件的读取")
    def write(self):
        print("excel文件的写入")

class Csv(AllFile):
    # 重写
    def read(self):
        print("csv文件的读取")
    def write(self):
        print("csv文件的写入")

t = Txt()
t.read()
t.write()

"""
注意
    1.抽象类中可以提供普通实例函数和抽象函数,不能实例化对象,
      但是,一般情况下,使用抽象类,只会在其中提供抽象方法
    2.抽象类中的抽象方法一般只是一个接口,不需要实现,具体的实现需要在子类中完成
    3.如果一个类继承自抽象类,但是未重写抽象类中的抽象方法,则该类仍然是一个抽象类
"""