1.###继承
"""
(1)单继承
至少2个类,子类 和父类
一个类继承另外一个类,当前类是子类(衍生类)
被继承的这个类是父类,(基类和超类)

Python所有类的父类都是object
"""
#1.子类继承后,子类可以使用父类的公有方法
class Father():
   skin="黑色的"
   __sex="man"
   def hobby(self):
      print("爱好打篮球")
   def __smoke(self):
      print("爱好抽大麻")

class Daughter(Father):
   pass

obj=Daughter()
print(obj.skin)
obj.hobby()

#2.子类继承后,子类不可以使用父类的私有方法
class Son(Father):
   def pub_func(self):
      print(self.__sex)
obj=Son()
# print(obj.__sex)   AttributeError
# print(obj.__smoke)   AttributeError
print(obj.skin)
# obj.pub_func()   AttributeError
#3.子父继承后,子类可以改写父类方法
"""self对象优先调用本类的方法,如果本类当中没有,再调用父类...."""
class not_child(Father):
   skin = "白色的"
   def hobby(self):
      print("白种人喜欢种大麻")
obj=not_child()
print(obj.skin)
2.####多继承
# 1.基本语法
class Father():
   f_property="英俊潇洒,才华横溢,道貌岸然,一表人才"
   def hobby(self):
      print("抽烟,喝酒,烫头,吃喝嫖赌")

class Mother():
   m_property = "美丽大方,漂亮温柔"
   def hobby(self):
      print("麦包包,打麻将,蹦野迪")
class Daughter(Father,Mother):
   pass

obj=Daughter()
print(obj.f_property)
obj.hobby()     #优先继承爸爸,所以继承爸爸的hobby

# 2.self 和 super的区别
"""
(1)super本身是一个类 super()是一个对象 用于调用父类的绑定方法
(2)super() 只应用在绑定方法中,默认自动传递self对象 (前提:super所在作用域存在self)
(3)super用途: 解决复杂的多继承调用顺序   
"""
class Father():
   f_property="英俊潇洒,才华横溢,道貌岸然,一表人才"
   def hobby1():
      print("抽烟,喝酒,烫头,吃喝嫖赌")

class Mother():
   m_property = "美丽大方,漂亮温柔"
   def hobby2(self):
      print("麦包包,打麻将,蹦野迪")
class Son(Father,Mother):
   m_property = "一枝梨花压海棠"
   def hobby2(self):
      print("出入奇怪的场所,比如卡丁车,蹦极")
   def skill1(self):
      print(Father.f_property)
      # self.hobby1()   error
      Father.hobby1()
   """
   self 在调用成员的时候,如果自己本类有,优先调用自己的
   self 在调用成员的时候,如果自己本类无,调用父类的
   """
   def skill2(self):
      print(self.m_property)
      self.hobby2()

   """
   #super
   super在调用的时候,一定会优先调用父类的方法或者属性
   这是它与self的本质区别
   """

   def skill3(self):
      print(super().m_property)
      super().hobby2()    #优先调用父类的
obj=Son()
obj.skill1()
obj.skill2()
obj.skill3()
3.#菱形继承(钻石继承)
"""
   Human
Man          Woman
   Children
"""
class Hunam():
   pty=1
   def feelT(self):
      print("古代人类,天冷了,穿动物的毛1")
      print(self.pty)
      print("古代人类,天热了,坑树皮2")
class Man(Hunam):
   pty = 2
   def feelT(self):
      print("现代人类,天热了,喝啤酒,吃西瓜3")
      super().feelT()
      print("现代人类,天冷了,喝啤酒,吃火锅4")
class Woman(Hunam):
   pty = 3
   def feelT(self):
      print("现代女性,天热了,穿裙子,光膀子,麦包包5")
      super().feelT()
      print("现代女性,天冷了,穿动物的毛,貂皮大衣6")
class Children(Man,Woman):
   pty = 4
   def feelT(self):
      print("现代小孩,天热了,吃冰棍,游泳7")
      super().feelT()
      print("现代小孩,天冷了,喝热水,玩农药8")
obj=Children()
obj.feelT()

"""
super  遵循mro列表中出现顺序,一次调用
类.mro
"""
lst=Children.mro()
print(lst)
"""
super 在调用同名方法时,要依据该列表中出现的类的调用顺序,依次调用
[<class '__main__.Children'>, <class '__main__.Man'>, <class '__main__.Woman'>, <class '__main__.Hunam'>, <class 'object'>]
"""

"""
现代小孩,天热了,吃冰棍,游泳7
现代人类,天热了,喝啤酒,吃西瓜3
现代女性,天热了,穿裙子,光膀子,麦包包5
古代人类,天冷了,穿动物的毛1
4
古代人类,天热了,坑树皮2
现代女性,天冷了,穿动物的毛,貂皮大衣6
现代人类,天冷了,喝啤酒,吃火锅4
现代小孩,天冷了,喝热水,玩农药8
"""
4.# ###多态
"""
不同的子类对象,调用相同的父类方法,产生不同的执行结果
继承,重写
好处:多态针对的是对象来说的,在不改变原有代码的前提下,完成不同的功能
不同的对象,相同的方法,大道了不同的功能。
"""
class Soldier():
   def attact(self):
      pass
   def back(self):
      pass

class Army(Soldier):
   def attact(self):
      print("[陆军]拿一把枪,突突突,然后倒下!")
   def back(self):
      print("[陆军]撒腿就跑,躺下装死")

class Navy(Soldier):
   def attact(self):
      print("[海军]开炮,鱼雷,拿鱼叉子插死敌人")
   def back(self):
      print("[海军]立即跳海,下海喂鱼!")

class Airforce(Soldier):
   def attact(self):
      print("[空军]空对地导弹,使用二营长的意大利炮,射死敌人")
   def back(self):
      print("[空军]立即跳伞,落地成盒!")

#实例化一个陆军士兵
obj_army=Army()
#实例化一个海军士兵
obj_navy=Navy()
#实例化一个空军士兵
obj_airforce=Airforce()

strvar="""
1.全体出击。
2.全体撤退。
3.空军上,其他撤退。
"""

lst=[obj_army,obj_navy,obj_airforce]
while True:
   print(strvar)
   num = input("请发号施令>>>:")
   if num.isdecimal():
      if int(num)==1:
         for i in lst:
            i.attact()
      elif int(num)==2:
         for i in lst:
            i.back()
      elif int(num)==3:
         for i in lst:
            if isinstance(i,Airforce):
               i.attact()
         else:
            i.back()
      else:
         print("风太大,听不请.....")

   else:
      if num.upper()=="Q":
         print("指挥完毕,请下次再来!")
5.###__new__魔术方法
"""
   触发时机:实例化类生成对象的时候触发(触发时机在__int__之前)
   功能:控制对象的创建过程
   参数:至少一个cls接受当前的类,其他根据情况决定
   返回值:通常返回对象或None
"""
"""
对象.属性
对象.方法()

类.属性
类.方法()

object 是所有类的父类
"""
#(1)基本语法
class MyClass():
   a=5

obj2=MyClass()
print(obj2)
class MyClass2():
   def __new__(cls):
      print(cls)       #<class '__main__.MyClass2'>
      #通过父类object中的__new__魔术方法,返回该类的对象,参数是类
      # obj=object.__new__(cls)
      #返回本类对象,只能通过父类创建
      #return obj
      #可以返回None
      return obj2
obj=MyClass2()
print(obj)       #<__main__.MyClass2 object at 0x000000000220FF60>
print(obj.a)      #5

#对比__new__和__init__之间的区别
"""
__new__  负责创建对象
__init__  负责初始化对象
__new__在__init__ 触发时机之前
"""
#__new__和__init__形参实参要匹配
class MyClass3():
   #用收集参数,一劳永逸
   def __new__(cls, *args, **kwargs):
      print(1)
      return object.__new__(cls)
   def __init__(self,name,age,sex):
      print(2)
      self.name=name
      self.age=age
      self.sex=sex
obj=MyClass3("Alex",27,"未知")
print(obj.name)
print(obj.age)
print(obj.sex)

#(3)注意点:__init__只能初始化自己本类的对象
class MyClass4():
   def __new__(cls, *args, **kwargs):
      print(1)
      return obj2
   #不会调用init方法,因为返回的不是没有class 本身的,不调用
   def __init__(self):
      print(2)
obj=MyClass4()
print(obj)    #<__main__.MyClass object at 0x0000000001E7FFD0>   不会打印2
6.###单态模式
"""
类无论实例化多少次,都有且只有一个对象,
"""
#1.基本语法
class SingTon():
   # 防止类外调用__obj,形成封装保护.
   __obj = None
   def __new__(cls, *args, **kwargs):
      if cls.__obj is None:
         cls.__obj=object.__new__(cls)
      return cls.__obj
   def __init__(self):
      pass

# 第一次实例化对象,因为cls.__obj is None 条件为真,创建对象并返回
obj1=SingTon()
# 第二次发现 cls.__obj is None: 条件为假,直接返回原有对象
obj2=SingTon()
# 第三次发现 cls.__obj is None: 条件为假,直接返回原有对象
obj3=SingTon()
print(obj1)       #<__main__.SingTon object at 0x00000000027986A0>
print(obj2)       #<__main__.SingTon object at 0x00000000027986A0>
print(obj3)       #<__main__.SingTon object at 0x00000000027986A0>
#2.  有且只有一个对象
class SingTon():
   # 防止类外调用__obj,形成封装保护.
   __obj = None
   def __new__(cls, *args, **kwargs):
      if cls.__obj is None:
         cls.__obj=object.__new__(cls)
      return cls.__obj
   def __init__(self,name):
      self.name=name
obj1=SingTon("Alex")
obj2=SingTon("Jack")
obj3=SingTon("Jane")
print(obj1.name)      #Jane
print(obj2.name)      #Jane
print(obj3.name)      #Jane
7.# ###__del__魔术方法(析构方法)
import os
"""
触发时机:当对象被内存回收的时候,自动触发[1.页面执行完毕,回收所有变量;2.所有对象被del的时候]
功能:对象使用完毕后资源回收
参数:一个self接受对象
返回值:无
"""
class LangDog():
   def __init__(self,name):
      self.name=name
   def eatmeat(self):
      print("可爱的小狼狗喜欢吃肉")
   def __del__(self):
      print("析构方法被触发!")

obj=LangDog("Janmes.dandan")
#(1).页面执行完毕,回收所有变量;
print("======程序执行完毕start=======")

#(2)所有对象被del的时候
obj2=obj   #两个不同的对象指向同一个对象
print(obj2)
print(obj)
del obj
print("======程序执行完毕end=======")

#(3)文件读取操作
"""
fp=open("文件名",mode="r",encoding="utf-8")
res=fp.read()
fp.close()
"""
class ResdFile():
   def __new__(cls, filename):
      if os.path.exists(filename):
         return  object.__new__(cls)
      return print("该文件不存在")
   def __init__(self,filename):
      self.fp=open(filename,mode="r",encoding="utf-8")

   def readcontent(self):
      res=self.fp.read()
      return res

   def __del__(self):
      fp.close()

obj=ResdFile("ceshi111.txt")
res=obj.readcontent()
print(res)
8.# ###__str__  魔术方法
"""
触发时机:使用print(对象)或者str (对象)的时候触发
功能: 查看对象
参数:一个self接受当前对象
返回值:必须返回字符串类型
"""
class Cat():
   gift="抓老鼠"
   def __init__(self,name):
      self.name=name
   def obj_info(self):
      return "该对象名字{},它天生属性是{}".format(self.name,self.gift)
   def __str__(self):
      return self.obj_info()
tom=Cat("汤姆")
#方式一:打印对象时触发
print(tom)       #该对象名字汤姆,它天生属性是抓老鼠
#方法二:强转对象为字符串时,触发
res=str(tom)
print(res)    #该对象名字汤姆,它天生属性是抓老鼠
9.###__repr__  魔术方法
"""
触发时机:使用repr(对象)的时候触发
功能: 查看对象
参数:一个self接受当前对象
返回值:必须返回字符串类型
"""
class Mouse():
   gift="会打洞"
   def __init__(self,name):
      self.name=name

   def obj_info(self):
      return "该对象名字{},该对象属性:龙生龙,凤生凤,老鼠的儿子{}".format(self.name,self.gift)

   def __repr__(self):
      return self.obj_info()
   #系统底层,自动加了一句话,如果存在__repr__魔术方法,自动把这个方法赋值给__str__方法,所以即使在print打印或者强转对象为字符串,也仍然触发
   __str__=__repr__
jerry=Mouse("杰瑞")
res=repr(jerry)
print(res)       #该对象名字杰瑞,该对象属性:龙生龙,凤生凤,老鼠的儿子会打洞

res=str(jerry)
print(res)       #该对象名字杰瑞,该对象属性:龙生龙,凤生凤,老鼠的儿子会打洞
10.###__call__魔术方法
import math
"""
触发时机:把对象当做函数调用的时候自动触发
功能: 模拟函数化操作
参数:参数不固定,至少一个self参数
返回值:看需求
"""
#(1)基本语法
class MyClass():
   def __call__(self):
      print("__call__方法被调用")
obj=MyClass()
obj()

#(2)__call__魔术方法可以做一个统一的调用
class MakeCake():
   def __call__(self,something):
      print("我做的东西是{}".format(something))
      self.step1()
      self.step2()
      self.step3()

   def step1(self):
      print("和面,发酵,放点糖,放牛奶,等一天")
   def step2(self):
      print("扔进烤箱,考七七四十九天,炼丹")
   def step3(self):
      print("拿出来,切一切吃")

obj=MakeCake()
obj("蛋糕")
# obj.step1()
# obj.step2()
# obj.step3()

#(3) 默认系统int这个方法的实现
# int 浮点型 纯数字  字符串  布尔类型 它自己
class MyInt():
   def func(self,strvar):
      res=strvar.lstrip("0")
      if res=='':
         return 0
      print(res)
      return eval(res)
   def __call__(self, num):
      if isinstance(num,bool):
         if num==True:
            return 1
         else:
            return 0
      elif isinstance(num,int):
         return num
      elif isinstance(num,float):
         if num<0:
            return math.ceil(num)
         else:
            return math.floor(num)
      elif num.isdecimal():
         self.func(num)
      else:
         print("该类型不能判断")


myint=MyInt()
print(myint(False))          #0
print(myint(True))       #1
print(myint(4444))
print(myint(-3.15))       #3
print(myint("000000123"))        #123
print(myint("000000000"))
print(myint("000000090"))     #90
11.###__bool__魔术方法
"""
触发时机:使用bool(对象)的时候触发
功能: 强转对象
参数:一个self接受当前对象
返回值:必须是bool类型
"""
class MyClass():
   def __bool__(self):
      return True
obj=MyClass()
res=bool(obj)
print(res)

12.###__add__魔术方法  (与之相关的__add__反向加法)
"""
触发时机:使用对象进行运算相加的时候自动触发
功能: 对象运算
参数:两个对象参数
返回值:运算后的值
"""
class MyClass():
   def __init__(self,num):
      self.num=num
   #当对象在+加号的左侧时,自动触发;
   def __add__(self,other):
      return self.num*2+other

   def __radd__(self, other):
      return self.num*3+other
a=MyClass(3)
#self接收到a,other 接收到1  self.num*2+1=3*2+1=7
res=a+1
print(res)

b=MyClass(5)
#第一次参数永远接受的是对象,self接收的b,other接收的是2 ==>,   5*3+2=17
res=2+b
print(res)

#a+b
"""
先触发add魔术方法,self接受到a,other接收到b  3*2+b==>6+b
6+b 再次触发了radd魔术方法 self接收到b other 接收到6  5*3+6=21
"""

res=a+b
print(res)
13.###__len__魔术方法
"""
触发时机:使用len(对象)的时候自动触发
功能: 用于检测对象中或者类中某个内容的个数
参数:一个self接受当前对象
返回值:必须返回整型
"""
class MyClass():
   pty1=1
   __pty2=2
   pty3=3
   def func1(self):
      pass
   def func2(self):
      pass
   def __func3(self):
      pass
   def __len__(self):
      dic = MyClass.__dict__
      lst=[i for i in dic if not(i.startswith("__") and (i.endswith("__")))]
      return len(lst)
"""
lst=[]
for i in dic:
   if not(i.startswith("__") and (i.endswith("__"))):
      lst.append(i)
"""
obj=MyClass()
print(len(obj))
14.####装饰器
"""
替换旧函数,返回新函数,在不改变原有代码的前提下,为原函数拓展新功能
语法:@(语法糖)
"""
#装饰器的原型
def kuozhan(func):
   def newfunc():
      print("厕所前,蓬头垢面")
      func()
      print("厕所后,精神抖擞")
   return newfunc
def func():
   print("我是高富帅")
"""
把旧函数当成参数传递给kuozhan,返回一个新函数newfunc赋值给func
func由以前的旧函数变成了现在的新函数,就间接完成了功能上的拓展
而没有改变原代码
"""
func=kuozhan(func)  #func  ===> newfunc===>func()   <=======>newfunc()
func()
print("---------------")
#2.装饰器的基本写法
def kuozhan(func):
   def newfunc():
      print("厕所前,蓬头垢面")
      func()
      print("厕所后,精神抖擞")
   return newfunc
@kuozhan
def func():
   print("我是高富帅")
func()
"""
@有两个功能:
   1.可以吧下面的func()当成参数传递给装饰器kuozhan
   2.把kuozhan函数返回的新函数,用来替换旧函数func,
   相当于func=kuozhan(func)一模一样
"""
#3.装饰器的嵌套
def kuozhan1(func):
   def newfunc():
      print("厕所前,蓬头垢面1")
      func()
      print("厕所后,精神抖擞2")
   return newfunc

def kuozhan2(func):
   def newfunc():
      print("厕所前,饥肠辘辘3")
      func()
      print("厕所后,酒足饭饱4")
   return newfunc
@kuozhan2
@kuozhan1
def func():
   print("我是个屌丝!")

func()

"""
首先把func当成参数传递给kuozhan1这个装饰器
返回:
   def newfunc():
      print("厕所前,蓬头垢面")
      func()
      print("厕所后,精神抖擞")
   变成下面的样子
   @kuozhan2
      def newfunc():
      print("厕所前,饥肠辘辘")
      func() #把上一次装饰的函数拿到这里
      print("厕所后,酒足饭饱")
   结果为:3124
"""
print("--------------------")
#4.带有参数的装饰器
"""
原函数是几个参数,拓展之后就是几个参数
"""
def kuozhan(func):
   def newfunc(who,where):
      print("厕所前,衣衫褴褛")
      func(who,where)
      print("厕所后,衣冠禽兽")
   return newfunc
@kuozhan
def func(who,where):
   print("{}在{}解手.....!".format(who,where))
func("李祖清","鸟窝")
print("------------------")
#5.带有参数返回值的装饰器
def kuozhan(func):
   def newfunc(*args,**kwargs):     #*和**的魔术用法
      print("厕所前,冷冷清清")
      res=func(*args,**kwargs)
      print("厕所后,满嘴雌黄")
      return res
   return newfunc
@kuozhan
def func(*args,**kwargs):
   lst = []
   dic={"lzq":"李祖清","wb":"吴波","sl":"帅了"}
   print(args)
   # print(kwargs)
   """
   for k,v in kwargs.items():
      if k in dic:
         # print(k,v)
         res=dic[k] + "留下了" +v
         lst.append(res)
   """
   #用推导式
   lst=[dic[k] + "留下了" + v for k, v in kwargs.items() if k in dic ]
   return lst
   # print(lst)


# func(lzq="15g",wb="15kg",sl="15t")
res=func("电影院","游泳池","鸟窝",lzq="15g",wb="15kg",sl="15t",lt="15mg")
print(res)
15.###类中方法:
"""
普通方法:没有任何参数,只能类来调用
绑定方法:
   (1)绑定到对象
   (2)绑定到类
静态方法:
   无论对象还是类都能调用
"""
class Dog():
   def jiao():
      print("小狗喜欢叫")
   #绑定方法(绑定对象)
   def run(self):
      print("小狗喜欢跑")
   #绑定方法(绑定类)
   @classmethod
   def tail(cls):
      print("小狗看见主人摇尾巴")
   #静态方法
   @staticmethod
   def tian():
      print("小狗看见骨头喜欢舔一舔")
#实例化对象
obj=Dog()
#1.普通方法
Dog.jiao()

#2.绑定方法(绑定对象)
obj.run()
Dog.run(1)   #手动传参

#3.绑定方法(绑定到类)
"""无论是类还是对象都可以调用,只不过默认传递的参数是类。"""
obj.tail()
Dog.tail()   #推荐

#4.静态方法
obj.tian()
Dog.tian()

#默认在类外,动态添加的方法是静态方法
obj.func=lambda :print(12333)
obj.func()

# import types (复习)
# types.MethodType