• 什么时候用组合什么时候用继承: 老师类 和 生日类 老师的生日 老师.生日 用组合

    老师类 和 人类 老师是人 class老师类(人类) 用继承 另外,有相同属性也可判断要用继承

    组合

    一个对象的属性值是另外一个类的对象 一个类的属性 用另一个类的对象来描述 两连点:alex.weapon.hand18(jin) alex的weapon属性,因为是一个另类的对象,weapon也有自己的方法,故两连点

    # 人狗大战
    class Dog:
       def __init__(self,name,aggr,hp,kind):
           self.name = name
           self.aggr = aggr   #武力值
           self.hp = hp    #血
           self.kind = kind
       #咬
       def bite(self,person):
           person.hp -= self.aggr

    class Person:
       def __init__(self,name,aggr,hp,sex):
           self.name = name
           self.aggr = aggr
           self.hp = hp
           self.sex = sex
           self.money = 0

       def attack(self,dog):
           dog.hp -= self.aggr

       #获取装备
       def get_weapon(self,weapon):
           if self.money >= weapon.price:
               self.money -= weapon.price
               self.weapon = weapon
               self.aggr += weapon.aggr
           else:
               print("余额不足,请先充值")
    #装备
    class Weapon:        
       def __init__(self,name,aggr,njd,price):
           self.name = name  
           self.aggr = aggr  #武力值
           self.njd = njd    #耐久度
           self.price = price   #价格

       #技能:18掌
       def hand18(self,person):
           if self.njd > 0:
               person.hp -= self.aggr * 2
               self.njd -= 1

    alex = Person('alex',0.5,100,'不详')
    jin = Dog('金老板',100,500,'teddy')
    w = Weapon('打狗棒',100,3,998)  #创造武器w
    # alex装备打狗棒
    alex.money += 1000
    alex.get_weapon(w)  #alex获取武器w
    print(alex.weapon)  
    #self.weapon = weapon即传入的w Alex的武器属性是武器的对象
    # alex.weapon 是 Weapon类的对象
    print(alex.aggr)
    alex.attack(jin)
    print(jin.hp)
    alex.weapon.hand18(jin) #武器的大招,传入jin jin掉血
    print(jin.hp)

    面向对象三大特性:继承 多态 封装

    继承

    class A:pass   # 父类,基类,超类
    class B(object):pass   # python3中,不写(类)默认继承object 里面很多双下方法
    class A_son(A):pass # 子类,派生类
    class AB_son(A,B):pass # 子类,派生类 多继承
    # 一个类 可以被多个类继承
    # 一个类 可以继承多个父类 —— python里

    print(A_son.__bases__) #查看继承自谁
    print(AB_son.__bases__)
    print(A.__bases__) # >>>object     python3 -新式类# 没有继承父类默认继承object

    避免大量重复代码 动物定义所有动物的共性 人猪狗继承自动物,各自有各自的方法,这些方法是人猪狗都有的 具体的实例继承自人或猪或狗,再添加自己的独特属性

    面试题:当子类没有init,但父类子类都有func方法,若子类实例化,调用的是谁的func

    # 狗类 吃 喝 看门(guard)
    # 鸟类 吃 喝 下蛋(lay)
    class Animal:
       def __init__(self):
           print('执行Animal.__init__')
           self.func()
       def eat(self):
           print('%s eating'%self.name)
       def drink(self):
           print('%s drinking'%self.name)
       def func(self):
           print('Animal.func')

    class Dog(Animal):
       def guard(self):
           print('guarding')
       def func(self):
           print('Dog.func')
    dog = Dog() #自己没有__init__ 就用父类的
    #Dog实例化触发父类__init__时,执行了self.func(),而父类子类都有func
    #因为在Dog实例化时,先触发__new__生成Dog的self,然后才去找__init__,进而找父类
    #父类要求执行self.func,而dog里是有func的,自然调用自己的

    单继承 后 派生新属性和方法

    父类 子类都有初始化方法,但是子类的初始化里 会让父类执行init,进而让子类的self获取父类中的属性和方法 除此之外,子类中初始化的其他属性和新定义方法就是派生的属性和方法 方法1 父类名.init(self, 属性1,属性2)

    class Animal:
       def __init__(self,name,aggr,hp):
           self.name = name
           self.aggr = aggr
           self.hp = hp

       def eat(self):
           print('吃药回血')
           self.hp+=100

    class Dog(Animal):
       def __init__(self,name,aggr,hp,kind):
           Animal.__init__(self,name,aggr,hp)  #这里的self全程都是Dog的self
                                               #若没有这句,狗就只有kind属性,因为自己有__init__
                                               #不会去找父类,进而无法初始化出self.name = name等
           
           self.kind = kind  # 派生属性:原来属性基础上添加的属性
       def eat(self):
           Animal.eat(self)   # 如果想在父类eat方法基础上添加新内容,需要在子类中调用父类,再加
           self.teeth = 2
       def bite(self,person):   # 派生方法:原来属性基础上添加的方法
           person.hp -= self.aggr

    jin = Dog('金老板',100,500,'吉娃娃')
    jin.eat()
    print(jin.hp)

    class Person(Animal):
       def __init__(self,name,aggr,hp,sex):
           Animal.__init__(self,name,aggr,hp)
           self.sex = sex       # 派生属性
           self.money = 0       # 派生属性

       def attack(self,dog):
           dog.hp -= self.aggr

       def get_weapon(self,weapon):
           if self.money >= weapon.price:
               self.money -= weapon.price
               self.weapon = weapon
               self.aggr += weapon.aggr
           else:
               print("余额不足,请先充值")
    alex = Person('alex',1,2,None)
    alex.eat()
    print(alex.hp)

    jin.bite(alex)
    print(alex.hp)
    # 父类中没有的属性 在子类中出现 叫做派生属性
    # 父类中没有的方法 在子类中出现 叫做派生方法
    # 只要是子类的对象调用,子类中有的名字 一定用子类的,子类中没有才找父类的,若父类爷爷类也没有则报错
    # 如果父类 子类都有的 用子类的

       # 如果想用父类的再加新内容,单独调用父类的:
       #       方法1: 父类名.方法名 需要自己传self参数
       #       方法2: super().方法名 不需要自己传self
      (父类实例化后调用父类__init__方法,当然不用再传self)
    # 正常的代码中 单继承 === 减少了代码的重复
    # 继承表达的是一种 子类是父类的关系

    方法2 super().init(属性1,属性2) super单继承很好用

    class Animal:
       def __init__(self,name,aggr,hp):
           self.name = name
           self.aggr = aggr
           self.hp = hp
       def eat(self):
           print('吃药回血')
           self.hp+=100

    class Dog(Animal):
       def __init__(self,name,aggr,hp,kind):
           super().__init__(name,aggr,hp)  # 只在新式类中有,python3中所有类都是新式类
           #super(Dog, self).__init__(name,aggr,hp)     ()中默认传了本类即Dog的self
           #父类实例化后调用父类__init__方法时,不用传self,只需传其他参数
           
           self.kind = kind       # 派生属性
       def eat(self):print('dog eating')

    # jin = Dog('金老板',200,500,'teddy')
    # print(jin.name)
    # jin.eat()
    super(Dog,jin).eat()  #super在类外面也能用,调用的是父类的eat方法

    多继承

    以下全是python3的 mro: 能直接查看找的顺序,新式类才有mro 就连super() 也是严格按照mro顺序查找的 总结:有共同父类则广度优先,没有共同父类则深度 钻石继承:广度优先情况 (两条路都能找到A则先找BC) B(A) , C(A) , D(B,C) D.func时,D没有func时,优先找B,B没有找C,C没有最好找A D先找左边父类,再找右边父类,再找爷爷类

    漏斗问题 只有B才是A的子类,B找了不去找A ,去找C的话就找不到了

    小乌龟问题(两条路都能找到F,且是太爷爷,则F最后找)

    # 
    # 新式类: 继承object类的才是新式类 广度优先 python3全是新式类
    # 经典类: 在2.7中直接创建一个类就是经典类 深度优先  

    # 新式类才有mro ,能直接查看顺序
    # print(D.mro())
    # D.mro()

    # 单继承 : 子类有的用子类 子类没有用父类
    # 多继承中,我们子类的对象调用一个方法,默认是就近原则,找的顺序是什么?
    # 经典类中 深度优先
    # 新式类中 广度优先
    # python2.7 新式类和经典类共存,新式类要继承object
    # python3   只有新式类,默认继承object
    # 经典类和新式类还有一个区别 mro方法只在新式类中存在
    # super 只在新式类存在
    # super的本质 :不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的

    加上super的多继承问题 证明:super的本质 不是单纯找父类 而是根据调用者的节点位置的广度优先顺序来的 结果为:打印A C B D

    多继承用super()时,super()是按照mro列表的顺序来查找父类 按道理图中A类里super()应该去找object了,但不是,是按照C.mro的顺序找的

     

    多态

    其他强类型语言的多态: 定义A类 并有一个x方法 B类继承自A类 有x方法,并写了x的具体实现方式 C类继承自A类 有x方法,并写了x的具体实现方式 B或者C的实例虽然调用的都是x方法,但是不同实例调用结果不同 其他语言需要有个A类,来让B类C类知道是一家子,x是关联的

    python的多态:不需要先定义A类,但需要定义x方法,且 x(obj) 里面会调用 真实实例的x方法,即B或C中具体的x方法

    封装 的思想

    封装:打包(类)+保密(私有__属性) 把一个包含了很多私有属性和私有方法的类 写好后,可在别的.py文件中把它导入进来

    单下划线_和双下划线__的约定(仅仅是约定,实在带下划线调用其实可以调到的):

    1. 单表示隐藏起来的属性,类的外部不能用(封装要明确区分内外)

    2. 双下滑线开头的属性,python会重命名属性;例如AAA类中的xxx,会重命名为_AAAxxx 只是原来的属性名无法调用,并不是真的无法访问

    例1

    # 广义上面向对象的封装 :代码的保护,面向对象的思想本身就是一种
    # 只让自己的对象能调用自己类中的方法

    # 狭义上的封装 —— 面向对象的三大特性之一
    # 属性 和 方法都藏起来 不让你看见
    class Person:
       __key = 123  # 私有静态属性
       def __init__(self,name,passwd):
           self.name = name
           self.__passwd = passwd   # 私有属性

       def __get_pwd(self):         # 私有方法
           return self.__passwd   #只要在类的内部使用私有属性,就会自动的带上_类名

       def login(self):          # 正常的方法调用私有的方法
           self.__get_pwd()

    alex = Person('alex','alex3714')
    print(alex._Person__passwd)   # _类名__属性名   alex.__dict__中也能看到
    print(alex.get_pwd())
    # 所有的私有 都是在变量的左边加上双下划綫
       # 对象的私有属性
       # 类中的私有方法
       # 类中的静态私有属性
    # 所有的私有的 都不能在类的外部使用

    例2


    __author__ = 'Linhaifeng'

    class Room:
       def __init__(self,name,owner,width,length,high):
           self.name=name
           self.owner=owner
           self.__width=width
           self.__length=length
           self.__high=high

       def tell_area(self): #此时我们想求的是面积
           return self.__width * self.__length *self.__high

       def tell_width(self):
           return self.__width


    r1=Room('卫生间','alex',100,100,10000)

    # arear=r1.__width * r1.__length #会报错,只能在类里才能用双下__直接访问该函数
    print(r1.tell_area())

    例3

    #为外部修改私有属性 提供方法
    class Room:
       def __init__(self,name,length,width):
           self.__name = name
           self.__length = length
           self.__width = width
       def get_name(self):
           return self.__name
       def set_name(self,newName):
           if type(newName) is str and newName.isdigit() == False:
               self.__name = newName
           else:
               print('不合法的姓名')
       def area(self):
           return self.__length * self.__width

    jin = Room('金老板',2,1)
    print(jin.area())
    jin.set_name('2')
    print(jin.get_name())

    私有属性补充 父类的私有属性 不能被子类调用

    class Foo:
       __key='123'
       
    class Son(Foo):
       print(Foo.__key)

    #用到私有属性的场景
    1.隐藏属性,不想被类外部调用
    2.想保护这个属性,不想被随意改变
    3.保护此属性,不被子类继承

    设计模式(编程思想):接口类 抽象类

    共有目的:规范子类,都是面向对象的开发规范 python中原生没有接口类说法,因为自带多继承,可用class模拟实现接口类

    java不支持多继承,但借助Interface 可以实现多继承概念,故有了接口类的说法

    首先看类的多继承

    接口类和接口不是一回事

    接口类 (多继承不好控制) 每个类要实现不同功能,即便是相同功能,每个类的实现方式也不同 默认多继承,接口类中的所有的方法都必须不能实现 @abstractmethod : 规定子类必须有下句的方法,方法里内容pass就行了,具体实现 在具体的Tiger类里实现即可

    例如tiger类继承自 必须有走方法的类 和 必须有 游方法的类,那么tiger类必须定义走方法和游方法,否则,当tiger实例化时,会报错提醒 少写了某个必要方法

    接口隔离原则:使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些不需要的接口

    抽象类 设计模式:抽象类 不支持多继承,抽象类中方法可以有一些代码的实现 解决 多个 实现功能很相近的类

    两种思想的比较: 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现 多继承的情况 由于功能比较复杂,所以不容易抽象出 相同功能的具体实现 去写在父类中

    #一切皆文件
    import abc #利用abc模块实现抽象类

    class All_file(metaclass=abc.ABCMeta):
       all_type='file'
       @abc.abstractmethod #定义抽象方法,无需实现功能
       def read(self):
           '子类必须定义读功能'
           with open('filaname') as f:
               pass

       @abc.abstractmethod #定义抽象方法,无需实现功能
       def write(self):
           '子类必须定义写功能'
           pass

    class Txt(All_file): #子类继承抽象类,但是必须定义read和write方法
       def read(self):
           print('文本数据的读取方法')
       def write(self):
           print('文本数据的读取方法')

    class Sata(All_file): #子类继承抽象类,但是必须定义read和write方法
       def read(self):
           print('硬盘数据的读取方法')

       def write(self):
           print('硬盘数据的读取方法')

    class Process(All_file): #子类继承抽象类,但是必须定义read和write方法
       def read(self):
           print('进程数据的读取方法')

       def write(self):
           print('进程数据的读取方法')

    wenbenwenjian=Txt()

    yingpanwenjian=Sata()

    jinchengwenjian=Process()

    #这样大家都是被归一化了,也就是一切皆文件的思想
    wenbenwenjian.read()
    yingpanwenjian.write()
    jinchengwenjian.read()

    print(wenbenwenjian.all_type)
    print(yingpanwenjian.all_type)
    print(jinchengwenjian.all_type)
    # 抽象类 : 规范
    # 一般情况下 单继承 能实现的功能都是一样的,所以在父类中可以有一些简单的基础实现
    # 多继承的情况 由于功能比较复杂,所以不容易抽象出 相同功能的具体实现 去写在父类中

    # 抽象类还是接口类 : 面向对象的开发规范 所有的接口类和抽象类都不能实例化
    # java :
    # java里的所有类的继承都是单继承,所以抽象类完美的解决了单继承需求中的规范问题
    # 但对于多继承的需求,由于java本身语法的不支持,所以创建了接口Interface这个概念来解决多继承的规范问题

    # python
    # python中没有接口类 :
     # python中自带多继承 所以我们直接用class来实现了接口类
    # python中支持抽象类 : 一般情况下 单继承 不能实例化
     # 且可以实现python代码

    内置方法

    内置方法:property(静态属性) classmethod(类方法) staticmethod(静态方法) 1.@property 作用:obj.方法() 得出结果 上一行加了@property后,可将方法伪装成属性,以后调用直接用 obj.方法

    但有限制:这个方法() 中,不能跟参数 且不能直接改:obj.方法=值 会报错 有时候明明是个方法 动词,却得到的是个数值之类的 名词 可伪装成静态属性 如果想改,加@name(self, new_name)且只能是一个参数

    删除由方法伪装的属性 del 触发deleter方法 ,deleter下面是打印就是打印,有删除就删除,结果由写的代码定

    2.staticmethod(静态方法) 类中的方法需要传self,若需要在实例化之前不传任何对象,即可完成该函数功能,可以用静态方法 直接 类名.函数()即可完成功能的实现

    3.classmethod(类方法) 一般函数传的是self,self为实例,直接去改改的是实例属性,此时需要类方法 不需要对象即可操作值 当这个方法的操作只涉及静态属性使时,就应该使用classmethod来装饰此方法

    总结: 类方法和静态方法 都是类调用的 对象可以调用类方法和静态方法,一般情况下 推荐用类名调用 类方法 有一个默认的参数 cls 代表这个类 cls 静态方法 没有默认的参数 就像函数一样

    自省 反射

    #自省 反射
    hasattr(obj,'属性') #obj.属性 是否存在
    getattr(obj,'属性') #获取obj.属性   不存在则报错
    getattr(obj,'属性''默认值') #获取obj.属性   不存在不会报错,返回那个默认值
    setattr(obj,'属性''属性的值') #obj.属性=属性的值
    delattr(obj,'属性') #del obj.属性
    #注意:反射可以反射类的属性,
    hasattr(cls,'属性')等等   一切皆对象,类也是对象
    内置模块也能用
    反射自己当前模块的变量:print(getattr(sys.module['__main__'],'属性'))
    反射自己当前模块的函数:getattr(sys.module['__main__'],'函数'))()
    如果是被导入,又想反射被导入模块中的属性:getattr(sys.module[__name__],'属性') 防止被写死
    模块在不被导入时叫'__main__' , 如果是被导入,则叫这个模块的名字,而sys.module可以查看所有导入进来的模块,且为字典

     

    双下getattr,双下setattr,双下delattr

    #__getattr__,__setattr__,__delattr__
    obj点的方式去操作属性时触发的方法
    __getattr__: obj.属性 不存在时触发
    __setattr__: obj.属性=属性的值 时触发
    __delattr__: del obj.属性 时触发

     

     

    继承里的包装 与 组合方式实现授权 对比

    继承里的包装:自己定义的方法与父类同名,调用时用的自定制方法,其余方法用的原来类的方法

    class List(list):
    #List继承自标准类型list

    #重写append
       def append(self, p_object):
      #若是str则用继承来的append
           if type(p_object) is str:
               # self.append(p_object)
               super().append(p_object)
           #若不是str,执行自定义内容
           else:
               print('只能添加字符串类型')
       #自定义一个函数:显示 列表实例 的中间值        
       def show_midlle(self):
           mid_index=int(len(self)/2)
           return self[mid_index]

    # l2=list('hell oworld')
    # print(l2,type(l2)) >>>得到一串 字符串形式的字母 列表

    l1=List('helloworld')
    # print(l1,type(l1))     >>>type是继承来的方法
    # print(l1.show_midlle())   >>>show_midlle是自定义方法
    l1.append(1111111111111111111111)   #本来是有append方法,但被覆盖重写
    l1.append('SB')
    print(l1)

    而授权:也是一种包装,但不是通过继承实现的,是通过 双下getattr

    import time
    class FileHandle:
       def __init__(self,filename,mode='r',encoding='utf-8'):
           # self.filename=filename
           self.file=open(filename,mode,encoding=encoding)
           self.mode=mode
           self.encoding=encoding
       def write(self,line):
           print('------------>',line)
           t=time.strftime('%Y-%m-%d %X')
           self.file.write('%s %s' %(t,line))

       def __getattr__(self, item):
           # print(item,type(item))
           # self.file.read
           return getattr(self.file,item) #调read时触发,变成getattr(self.file,read)
           #因为self.file在__init__初始化成文件句柄,里面是有read方法,即触发文件里的read

    f1=FileHandle('a.txt','w+')
    # print(f1.file)
    # print(f1.__dict__)
    # print('==>',f1.read)     #read在FileHandle中找不到,即触发__getattr__
    # print(f1.write)           #write有定义,使用类已定义的write方法
    f1.write('1111111111111111 ')
    f1.write('cpu负载过高 ')
    f1.write('内存剩余不足 ')
    f1.write('硬盘剩余不足 ')
    # f1.seek(0)
    # print('--->',f1.read())
    #__getitem__,__setitem_,__delitem__
    obj[‘属性’]的方式去操作属性时触发的方法

    __getitem__:obj['属性'] 时触发
    __setitem__:obj['属性']=属性的值 时触发
    __delitem__:del obj['属性'] 时触发

    #__get__,__set__,__delete__
    描述符是一个新式类,这个类至少要实现上述三个方法的一个
    class 描述符:
    def __get__():
    pass
    def __set__():
    pass
    def __delete__():
    pass

    class 类:
    name=描述符()

    obj=类()
    obj.name #get
    obj.name='egon' #set
    del obj.name #delete

    #__del__:析构方法
    垃圾回收时自动触发

    双下方法:

    内置的类方法 和 内置的函数之间有着千丝万缕的联系

    转成字符串
    obj.__str__ = str(obj)

    repr
    print(1)   --->1
    print('1')  --->'1'
    obj.__repr__ = repr(obj)

    obj.__len__ = repr(len)

    较重要的:__call__

    class A:
       def __init__(self, name):
           pass
       def __call__(self):
           print('1111')
    a=A('alex')
    a()
    实例化的对象+() 即会执行__call__方法
    类进行实例化生成实例时候,就是执行__call__方法

    还有元类生成类的时候,利用了__call__

    构造方法new及单例模式


    #__init__初始化方法
    #__new__ 构造方法:创建一个对象,init之前就有self,而self是由__new__造出来的
    class A:
       def __init__(self):
           self.x = 1
           print('in init function')
       # 利用object类中的__new__方法造出新对象并return,return出来的就是self
       # def __new__(cls, *args, **kwargs): #cls默认参数代表本类,此时还没有self
       #     print('in new function')
       #     return object.__new__(A, *args, **kwargs)
       

    #实例出来的是不同的实例(内存地址不同)
    a1 = A()
    a2 = A()
    a3 = A()
    print(a1)
    print(a2)
    print(a3)
    print(a.x)


    # 设计模式的一种:单例模式
    # 一个类 始终 只有 一个 实例
    # 当你第一次实例化这个类的时候 就创建一个实例化的对象
    # 当你之后再来实例化的时候 就用之前创建的对象(操作的是同一个对象)
    class A:
       __instance = False  #私有属性
       def __init__(self,name,age):
           self.name = name
           self.age = age
       def __new__(cls, *args, **kwargs):    
       # __new__为构造方法,__init__初始化之前 先执行的就是构造方法,cls默认参数代表本类,此时还没有self,执行的结果即返回self,
         
         if cls.__instance:    #第二次进来才满足此条件
               return cls.__instance
           cls.__instance = object.__new__(cls)  #第一次进来   ,触发object类里的__new__并传入本类,创造出本类的子类

           return cls.__instance    #返回子类,即self

    egon = A('egg',38)
    egon.cloth = '小花袄'
    nezha = A('nazha',25)
    print(nezha)
    print(egon)
    print(nezha.name)
    print(egon.name)
    print(nezha.cloth)
    #__eq__:__eq__(slef,other) 判断self对象是否等于other对象

    class A:
       def __init__(self,name):
           self.name = name

       def __eq__(self, other):
           pass

    ob1 = A('egg')
    ob2 = A('egg')
    print(ob1 == ob2)  
    #>>>False 原因:虽然参数相同但还是两个实例,内存地址不相同
    #__eq__的self和other是实例的内存地址
    # ‘==’ 默认比较的其实是内存地址

    class A:
       def __init__(self,name):
           self.name = name

       def __eq__(self, other):
           if self.name == other.name_:
               return True
           else:
               return False
    ob1 = A('egg')
    ob2 = A('egg')
    print(ob1 == ob2)
    #>>>Ture
    #定义__eq__后,比较的是两个对象的值,而值是相同的,不用的只是对象
    只要是可hash的,里面都有__hash__方法

    class A:
       def __init__(self,name,sex):
           self.name = name
           self.sex = sex
    a = A('egon','男')
    b = A('egon','nv')
    c = A('egon','男')
    print(hash(a))
    print(hash(b))
    print(hash(c))
    #默认对对象的内存地址进行hash
    > -9223371859710366888
    > 177144408945
    > -9223371859710366860


    class A:
       def __init__(self,name,sex):
           self.name = name
           self.sex = sex
       def __hash__(self):
           return hash(self.name+self.sex)
    a = A('egon','男')
    b = A('egon','女')
    c = A('egon','男')
    print(hash(a))
    print(hash(b))
    print(hash(c))
    #改成对值的hash
    > 9213915433563894301
    > -546061345136987036
    > 9213915433563894301
  • 相关阅读:
    安装并配置Next主题
    Hexo + Next 主题博客提交百度谷歌收录
    如何让Hexo不渲染某些文件
    javascript简单应用——今日诗词
    快速免费的公用 CDN —— jsDelivr
    删除github文件/文件夹
    GitHub+jsDelivr+PicGo搭建快速免费图床
    用QQ聊天记录生成一个词云
    使用UptimeRobot对网站和服务器实时监控
    java 线程安全集合
  • 原文地址:https://www.cnblogs.com/lishuaing/p/11264279.html
Copyright © 2020-2023  润新知