zoukankan      html  css  js  c++  java
  • Python基础- 类和对象(使用、继承、派生、组合、接口、多态、封装、property、staticmethod、classmethod、反射、slots、上下文管理协议、元类)

     

    一、初识类和对象

    在python3中类型就是类 
    先定义类在产生相对应的对象,也就是现有了概念再有了实体

    class Garen: 
    camp = ‘Demacia’ 
    def attack(self): 
    print(‘attack’)

    1、如何使用类

    在python3:

    1、所有的类都是新式类,即默认都是继承object类

    在python2中:

    1、新式类,必须明确写出继承object类 
    2、经典类,没有写出继承object类

    #方式一:实例化
    x = int(10)
    print(10)
    
    obj = Garen()
    print(obj)
    
    #方式二:引用类的类的变量和类的函数
    Garen.camp

    2、如何使用对象(实例)

    对象的使用方法: 
    1、属性引用:对象名.属性名(对象的属性:变量和函数)

    class Garen:
        camp = 'Demacia'
        def __init__(self, nickname):
            self.nick = nickname
        def attack(self):
            print('attack')
    
    g1 = Garen('草丛伦') #会自动触发__init__函数
    g2 = Garen('猥琐伦')
    
    Garen.attack(123) #此处必须传参数,因为调用的是函数

    调用绑定方法,会把自己作为第一个参数传入 
    g1.attack() #此处不需传参就可以调用,系统会将self = g1,因为调用的是实例化后的g1对象; 
    所以,只要是有对象进行调用,就会触发自动传值,将对象作为第一个对象传入,也就是将g1传入self

    总结:

    类: 
    第一种用途-实例化 
    第二种用途-引用名字(类名.变量名、类名.函数名) 
    实例:引用名字(实例名.类的变量,实例名.绑定方法,实例名.实例自己的变量名)

    对象(实例): 
    对象的属性:对象本身就只有特征(变量) 
    对象的用法:属性引用 
    s1.id、s1.name、s1.sex、s1.province

    3、类与对象的名称空间及绑定方法

    类与对象的名称空间

    类的名称空间:Student.__dict__ 
    对象的名称空间:s1.__dict__

    对象在调用方法或属性时:先从对象的名称空间找,再从类的名称空间找

    tips: 
    类的变量是与对象共有的

    类与对象的绑定方法

    类的绑定方法与对象的绑定方法不共有: 
    绑定方法的核心在于”绑定”,唯一绑定一个确定的对象,谁调用就作用在谁上

    4、类的继承与派生

    继承

    1、什么是继承

    #python支持多继承,__bases__查看父类
    
    class ParentClass1:
        pass
    class ParentClass2:
        pass
    class SubClass1(ParentClass1):
        pass
    class SubClass2(ParentClass1,ParentClass2):
        pass

    2、继承与抽象(先抽象再继承): 
    抽象即抽取类似或者说比较像的部分 
    抽象分成两个层次: 
    ① 
    ②将人、猪、狗这三个类比较像的部分抽取成父类 
    抽象最主要的作用是划分类别

    继承:是基于抽象的结果,通过编程语言去实现,肯定是先经历抽象这个过程,才能通过继承的方式去表达出抽象的结构

    例子:

    class Animal:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def walk(self):
            print('%s is walking' %self.name )
    
        def say(self):
            print('%s is saying' %self.name )
    
    class People(Animal):
        pass
    
    class Pig(Animal):
        pass
    
    class Dog(Animal):
        pass
    
    p1 = People('obama',50)
    p1 = People('obama',50)
    p1 = People('obama',50)

    3、类的继承原理 
    继承顺序: 
    类是经典类,多继承的情况下,会按照深度优先方式查找 
    类是新式类,多继承的情况下,会按照广度优先方式查找

    #广度优先查找方式:
    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        def test(self):
            print('from C')
    
    class D(B):
        def test(self):
            print('from D')
    
    class E(C):
        def test(self):
            print('from E')
    
    class F(D,E):
        def test(self):
            print('from F')
    
    f1 = F()
    f1.test()
    
    print(F.__mro__) #只有新式类中有这个方法,用来查看继承顺序(方法解析顺序MRO)
    
    #逐层注释
    #F—>D—>B—>E—>C—>A—>object:广度优先
    #深度优先查找方式:
    class A(object):
        def test(self):
            print('from A')
    
    class B(A):
        def test(self):
            print('from B')
    
    class C(A):
        def test(self):
            print('from C')
    
    class D(B):
        def test(self):
            print('from D')
    
    class E(C):
        def test(self):
            print('from E')
    
    class F(D,E):
        def test(self):
            print('from F')
    
    f1 = F()
    f1.test()
    
    
    #逐层注释
    #D—>B—>A—>E—>C:深度优先

    子类中调用父类的方法 
    方法一:父类名.父类方法

    方法二:super() 
    不使用super()的惨痛教训

    super()在python2中的用法: 
    1、super(自己的类,self).父类的函数名 
    2、super()只能用于新式类

    派生

    需求:子类派生的新方法是在父类的基础上变更,而不是整个重写 
    派生:子类继承了父类的属性,然后衍生出自己新的属性, 
    如果子类衍生出的新属性与父类的某个属性名字相同, 
    那么再调用子类的这个属性,就以子类自己这里的为准了 
    解决:父类名.方法

    5、组合

    代码重用的关系: 
    1、继承——什么’是’什么 
    2、组合——什么’有’什么

    class Teacher:
        def __init__(self,name,sex,course):
            self.name = name
            self.sex = sex
            self.course = course
    
    class Course:
        def __init__(self,name,price,peroid):
            self.name = name
            self.price = price
            self.peroid = peroid
    
    #课程
    python_obj = Course('python',15800,'7m')
    #老师'有'课程
    t = Teacher('egon','male',python_obj)

    6、接口与归一化设计与抽象类

    接口

    什么是接口: 
    python中没有接口的概念,用继承的方式来模拟接口

    接口的好处:归一化设计

    例:

    class Animal:
        def run(self):
            pass
    
        def speak(self):
            pass
    
    class People(Animal):
        def run(self):
            print('people is running!')
    
        def speak(self):
            print('people is speaking!')
    
    class Pig(Animal):
        def run(self):
            print('pig is running!')
    
        def speak(self):
            print('pig is speaking!')

    抽象类

    import abc
    
    class Animal(metaclass=abc.ABCMeta):
        @abc.abstractmethod #表示这个方法必须被子类实现
        def run(self):
            pass
    
        @abc.abstractmethod #表示这个方法必须被子类实现
        def speak(self):
            pass

    以上所有都是为了让python模拟实现类似于java中接口的功能,也就是定义一个接口类,接口类中有方法,所有继承接口类的子类必须定义接口类中的方法,不然报错

    6、多态与多态性

    多态:指一类事物的多种形态,就叫多态,并用继承的形式体现

    多态性:定义统一的接口-可以传入不同类型的值,但是调用的逻辑都一样,执行的结果却不一样

    多态性的好处: 
    1、增加灵活性 
    2、增加扩展性

    #多态
    class Animal:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    
        def walk(self):
            print('%s is walking' %self.name )
    
        def say(self):
            print('%s is saying' %self.name )
    
    class People(Animal):
        pass
    
    class Pig(Animal):
        pass
    
    class Dog(Animal):
        pass
    
    peo1 = People()
    pig1 = Pig()
    #多态性
    def func(obj):
        obj.run()
    
    func(peo1)
    func(pig1)

    结:

    多态:定义角度而言 
    多态性:使用角度而言

    7、封装

    要封装什么:数据的封装、方法的封装 
    为什么要封装: 
    封装数据的主要原因是:保护隐私 
    封装方法的主要原因是:隔离复杂度

    封装分为两个层面: 
    1、第一个层面的封装(什么都不用做):创建类和对象会分别创建二者的名称空间,我们只能用类名.或者obj.的方式去访问里面的名字,这本身就是一种封装 
    2、第二个层面的封装:类中把某些属性和方法隐藏起来(或者说定义成私有的),只在类的内部使用、外部无法访问,或者留下少量接口(函数)供外部访问。

    __名字,这种语法,只在定义的时候才会有变形的效果,如果类或者对象已经产生了,就不会有变形效果了

    __名字形式,不能被子类覆盖,因为在定义阶段名字的形式会变形,变为_类名名字,因为类名是父类的类名,子类不能改写,所以不能被子类覆盖

    8、property(特性)

    什么是特性(property) 
    property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值

    被property装饰的属性,如sex,分成三种 
    1、property 
    2、sex.setter 
    3、sex,deleter

    一旦一个属性被修饰之后,它就优先于对象

    import math
    class Circle:
        def __init__(self,radius):
            self.radius = radius
    
        def area(self):
            return math.pi * self.radius ** 2 #计算面积
    
        def perimeter(self):
            return 2 * math.pi * self.radius #计算周长
    
    c = Circle(7)
    print(c.radius)
    
    print(c.area())
    print(c.perimeter())
    

    上面的代码中,面积和周长应该是圆的一个属性,而不是一种绑定方法

    改:
    import math
    class Circle:
        def __init__(self,radius):
            self.radius = radius
    
        @property #area=property(area)
        def area(self):
            return math.pi * self.radius ** 2 #计算面积
    
        @property
        def perimeter(self):
            return 2 * math.pi * self.radius #计算周长
    
    加上装饰器(property)之后就变成了一个属性,不需要用()进行调用,而是直接和调用属性的方式一样
    print(c.area)
    print(c.perimeter)

    另一种玩法:

    class People:
        def __init__(self,name):
            self.__Name = name
    
        @property
        def name(self):
            return self.__Name
    
    p1 = People('cobila')
    print(p1.name)
    
    p1.name = 'egon' #报错的,因为它真实存在的位置是self.__Name
    
    #需求:被property装饰过的方法,用户想要主动修改
    
    class People:
        def __init__(self,name,sex):
            self.__Name = name
            self.__Sex = sex
    
        @property
        def sex(self):
            return self.__Sex
    
        @sex.setter
        def sex(self,value):
            #设定value的值为字符串
            if not isinstance(value,str):
                raise TypeError("性别必须是字符串类型!")
            self.__Sex = value
    
    
    p1 = People('cobila','male')
    p1.sex = 'female'
    print(p1.sex)
    

    类比:还有@sex.deleter

    9、staticmethod(静态方法)

    statimethod特点: 
    statimethod不与类或对象绑定,谁都可以调用,没有自动传值效果

    statimethod作用: 
    python为我们内置了函数staticmethod来把类中的函数定义成静态方法, 
    statimethod修饰过的方法就是给类用的,不与任何对象绑定,也就是说不是对象的绑定方法,对象不能调用(实际上也可以调用的到),就算对象调用了也是用类来调用的

    class Foo:
        def spam(self,x,y,z):
            print(x,y,z)
    
    Foo.spam(123)
    
    class Foo:
        @staticmethod
        def spam(x,y,z):
            print(x,y,z)
    
    Foo.spam(1,2,3)

     

    但凡是定义在类的内部,并且没有被任何修饰器修饰过的方法,都是绑定方法,有自动传值功能

    但凡是定义在类的内部,并且staticmethod修饰器修饰过的方法,都是解除绑定方法,实际上就是函数,没有自动传值的功能

    10、classmethod(类方法)

    例子中用于子类继承父类之后,调用方法的是父类,而应该是子类,所以要用到classmethod

    class Foo:
        @classmethod #把一个方法绑定给类,类.绑定到类的方法(),会把类本身当做第一个参数自动传递给绑定到类的方法
        def test(cls,x):
            print(cls,x) #拿到一个类的内存地址后,可以实例化或者引用类的属性
    
    Foo.test(123)
    
    __str__的用法:
    定义在类内部,必须返回一个字符串类型
    什么时候触发__str__执行:打印由这个类产生的对象时会触发执行
    
    class People:
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def __str__(self):
            return "<name:%s,age:%s>"%(self.name,self.age)
    
    p1 = People('egon',18)
    print(p1)

    *11、反射

    一、isinstance(obj,cls)检查是否obj是否是类 cls 的对象

    class Foo(object):
        pass
    
    obj = Foo()
    
    isinstance(obj, Foo)

    二、issubclass(sub, super)检查sub类是否是 super 类的派生类

    class Foo(object):
        pass
    
    class Bar(Foo):
        pass
    
    issubclass(Bar, Foo)

    三、反射:通过字符串的形式操作对象相关的属性;python中的一切事物都是对象(都可以使用反射) 
    反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。

    class People:
        country = 'China'
        def __init__(self,name):
            self.name = name
    
    p1 = People('egon')
    People.country

    反射的概念就是用字符串的形式来访问(调用)属性 
    1、

    hasattr(p,'name')
    #判断object中有没有一个name字符串对应的方法或属性
    print('name' in p.__dict__)
    
    print(hasattr(p,'name'))

    2、

    getattr(object, name, default=None)
    getattr(p,'xxxxx')
    • 1
    • 2

    3、setattr(x, y, v)

    4、delattr(x, y)

    5、反射当前模块属性

    import sys
    
    this_module = sys.modules[__name__] #拿到当前模块的对象
    
    hasattr(thsi_module,'xxx')
    getattr(thsi_module,'xxx')
    delattr(thsi_module,'xxx')

    四、反射的应用

    反射的精髓:通过字符串获取需要的属性 
    好处一:

    def add():
        print('add')
    
    def change():
        print('change')
    
    def search():
        print('search')
    
    def delete():
        print('delete')
    
    func_dict={
        'add':add,
        'change':change,
        'search':search,
        'delete':delete
    }
    
    while True:
        cmd = input(">>>").strip()
        if not cmd : continue
        if cmd in func_dict:  #hasattr()
            func = func_dict.get(cmd) #getattr()
            func()

    用反射改进:

    def add():
        print('add')
    
    def change():
        print('change')
    
    def search():
        print('search')
    
    def delete():
        print('delete')
    
    this_module = sys.modules[__name__]
    
    while True:
        cmd = input(">>>").strip()
        if not cmd : continue
        if hasattr(this_module,cmd):
            func = getattr(this_module,cmd)
            func()

    好处二:实现可插拔机制 
    有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类, 
    lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。 
    总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思? 
    即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

    好处三:动态导入模块(基于反射当前模块成员)

    12、attr系列

    __setattr__、__delattr__、__getattr__

    
    class Foo:
        x=1
        def __init__(self,y):
            self.y=y
    
        def __getattr__(self, item):
            print('----> from getattr:你找的属性不存在')
    
    
        def __setattr__(self, key, value):
            print('----> from setattr')
            # self.key=value #这就无限递归了,你好好想想
            # self.__dict__[key]=value #应该使用它
    
        def __delattr__(self, item):
            print('----> from delattr')
            # del self.item #无限递归了
            self.__dict__.pop(item)
    
    #__setattr__添加/修改属性会触发它的执行
    f1=Foo(10)
    print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
    f1.z=3
    print(f1.__dict__)
    
    #__delattr__删除属性的时候会触发
    f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
    del f1.a
    print(f1.__dict__)
    
    #__getattr__只有在使用点调用属性且属性不存在的时候才会触发
    f1.xxxxxx

    三者的用法演示:

    ①定制自己的数据类型 
    二次加工标准类型 
    基于继承的原理:来定制自己的数据类型(继承标准类型)

    class List(list):
        def append(self,p_object):
            if not isinstance(p_object,int):
                raise TypeError('must be int')
            super().append(p_object) #派生,调用父类的方法
    
    
    l = List([1,2,3])
    print(l)
    l.append(4)
    print(l)
    
    需求:不能用继承来实现open的功能
    基于授权的原理:实现授权的关键点就是覆盖__getattr__方法
    f = open('a.txt','w')
    print(f)
    
    
    class Open:
        def __init__(self,filepath,m='r',encode='utf-8'):
            self.x = open(filepath,mode=m,encoding=encode)
            self.filepath = filepath
            self.mode = mode
            self.encoding = encoding
    
        def write(self,line):
            t=time.strftime('%Y-%m-%d %X')
            self.x.write(t,line)
    
        def __getattr__(self,item): #找不到的时候触发getattr,去真实的文件句柄中去找
            return getattr(self.x,item)
    
    f = Open('b.txt','w')
    print(f)
    f.write('1111
    ')
    print(f.read())

    授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。 
    其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。 
    实现授权的关键点就是覆盖getattr方法

    练习一

    class List:
        def __init__(self,seq):
            self.seq=seq
    
        def append(self, p_object):
            ' 派生自己的append加上类型检查,覆盖原有的append'
            if not isinstance(p_object,int):
                raise TypeError('must be int')
            self.seq.append(p_object)
    
        @property
        def mid(self):
            '新增自己的方法'
            index=len(self.seq)//2
            return self.seq[index]
    
        def __getattr__(self, item):
            return getattr(self.seq,item)
    
        def __str__(self):
            return str(self.seq)
    
    l=List([1,2,3])
    print(l)
    l.append(4)
    print(l)
    # l.append('3333333') #报错,必须为int类型
    
    print(l.mid)
    
    #基于授权,获得insert方法
    l.insert(0,-123)
    print(l)

    练习二

    class List:
        def __init__(self,seq,permission=False):
            self.seq=seq
            self.permission=permission
        def clear(self):
            if not self.permission:
                raise PermissionError('not allow the operation')
            self.seq.clear()
    
        def __getattr__(self, item):
            return getattr(self.seq,item)
    
        def __str__(self):
            return str(self.seq)
    l=List([1,2,3])
    # l.clear() #此时没有权限,抛出异常
    
    
    l.permission=True
    print(l)
    l.clear()
    print(l)
    
    #基于授权,获得insert方法
    l.insert(0,-123)
    print(l)

    13、slots与迭代器协议

    在写大型框架的时候会用到 
    描述符(__get__,__set__,__delete__)

    一、__setitem__,__getitem,__delitem__

    class Foo:
        def __init__(self,name):
            self.name=name
    
        def __getitem__(self, item):
            print(self.__dict__[item])
    
        def __setitem__(self, key, value):
            self.__dict__[key]=value
    
        def __delitem__(self, key):
            print('del obj[key]时,我执行')
            self.__dict__.pop(key)
    
        def __delattr__(self, item):
            print('del obj.key时,我执行')
            self.__dict__.pop(item)
    
    f1=Foo('egon')
    f1['age']=18
    f1['age1']=19
    del f1.age1
    del f1['age']
    f1['name']='alex'
    print(f1.__dict__)

    三、 __next__和__iter__实现迭代器协议

    from collections import Iterable,Iterator
    
    class Foo:
        def __init__(self,start):
            self.start = start
    
        def __iter__(self):
            return self
    
        def __next__(self):
            # 这样的缺点就是初始值0不能得到,做以下改进
            # self.start += 1
            # return self.start
    
            #改进版,就可以返回0这个初始值了
            if self.start > 10 :
                raise StopIteration
            n = self.start
            self.start += 1
            return n
    
    
    f = Foo()
    f.__iter__()
    f.__next__()
    
    print(isinstance(f,Iterable))
    print(isinstance(f,Iterator))
    
    print(next(f))  #f.__next__()
    
    for i in f: # res = f.__iter__()  #next(res)  #直至抛出异常
    
    obj = f.__iter__()

    14、上下文管理协议

    一、 __del__ 
    析构方法,当对象在内存中被释放时,自动触发执行。 
    注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放, 
    因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    二、上下文管理协议

    with open('a.txt','r') as f:
        pass
    • 1
    • 2

    上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明enterexit方法

    class Open:
        def __init__(self,name):
            self.name=name
    
        def __enter__(self):
            print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
            # return self
    
        def __exit__(self, exc_type, exc_val, exc_tb): #type、value、traceback
            print('with中代码块执行完毕时执行我啊')
    
    
    with Open('a.txt') as f:  # Open('a.txt').__enter__
        print('=====>执行代码块')
        # print(f,f.name)
    
    exc_type = 类型(异常类型)
    exc_val = 值(异常值)
    exc_tb = 追踪信息
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    15、元类

    typer—->类—->对象 
    创建类的两种方式

    方式一:使用class关键字 
    方式二(就是手动模拟class创建类的过程):将创建类的步骤拆分开,手动去创建 
    准备工作:创建类主要分为三部分 
      1 类名 
      2 类的父类 
      3 类体

    步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间), 
    我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似, 
    只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典

    步骤二:调用元类type(也可以自定义)来产生类Chinense

    我们看到,type 接收三个参数: 
    第 1 个参数是字符串 ‘Foo’,表示类名 
    第 2 个参数是元组 (object, ),表示所有的父类 
    第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法 
    补充:若Foo类有继承,即class Foo(Bar):…. 则等同于type(‘Foo’,(Bar,),{})

    4 一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type, 
    用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的创建,工作流程是什么)

    自定制元类

    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            print(self)
            print(class_name)
            print(class_bases)
            print(class_dic)
            type.__init__(self,class_name,class_bases,class_dic)
    
    class Foo(metaclass=Mymeta): #指定Foo继承自定制的元类Mymeta
        x=1
        def run(self):
            print('running')
    
    要实现的效果:
    Foo = Mymeta('Foo',(object,),{'x':1,'run':run的内存地址})
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    实现定制类——需求:强制定义类的时候必须写注释

    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            print(self)
            print(class_name)
            print(class_bases)
            print(class_dic)
            type.__init__(self,class_name,class_bases,class_dic)
    
            for key in class_dic:
                if not callable(class_dic[key]):
                    continue
                if not class_dic[key].__doc__:
                    raise TypeError("没写注释,赶紧写!")
    
    class Foo(metaclass=Mymeta): #指定Foo继承自定制的元类Mymeta
        x=1
        def run(self):
            print('running')
  • 相关阅读:
    java 中的锁 -- 偏向锁、轻量级锁、自旋锁、重量级锁(转)
    MySQL存储引擎--MyISAM与InnoDB区别
    Socket详解
    Java线程池参数
    Java反射机制(转)
    java注解
    docker入门实例
    docker常用命令总结
    showdoc 自动脚本安装
    [mysql]You must reset your password using ALTER USER statement before executing this statement.
  • 原文地址:https://www.cnblogs.com/Huangsh2017Come-on/p/7498979.html
Copyright © 2011-2022 走看看