python详解(5)-面向对象与类

本文深入探讨了Python中的面向对象编程,包括类、封装、继承、多态等核心概念。详细阐述了类的定义、实例化过程,特别是init方法的作用。此外,还介绍了类属性、实例属性、静态方法、类方法的区别以及访问限制。文章强调了面向对象的基本理念,如通过创建实例和调用方法简化主流程,并讲解了继承和多态的实现。最后,提到了元类、单例模式以及ORM(对象关系映射)在Python OOP中的应用。
摘要由CSDN通过智能技术生成

本部分简单介绍了OOP的基本特性及其在python中的表示方法,并着重分析了python中类的一些方法和理念(若能全部掌握,则中端应用无问题)。

面向对象编程(Object Oriented Programming),简称OOP,是一种程序设计思想,对象包含了数据和操作数据的函数。
:是封装对象的属性和行为的载体,是OOP的基本对象。
封装:将对象的属性和行为封装起来,隐藏其实现细节,避免外部对内部数据的影响,提高了程序的可维护性。
继承:子类通过继承复用父类的属性和行为,并且可以添加子类独有的属性和行为。(即继承父类的所有属性与方法)
多态:子类继承父类特征的同时,具备了自己的特征,并且可以实现不同的效果。(即可以重写同名的属性和方法,并且优先级高)

面向对象的基本理念:将主流程简化为创建实例+调用实例方法,将所有需要的方法和属性封装进类中,将每个实例都需要的操作置入init初始化,制作基类用于放置共用的属性和方法,将所有其他需要基类方法的类继承基类。

类的定义:使用class来实现,包括有类名、类文档字符串和类体,与函数定义类似,但注意在python3中,当不继承某类时(其实还是继承自object类,但解释器将其划为默认),类名后不加括号注明参数。

类的实例:可将实例赋值给变量,a = Class(),注意定义实例时输入的参数为__init__方法除self(代表实例本身)以外的参数,a是一个类的实例对象。

init方法:是类中的魔法方法,作用为初始化,每一个实例都会自动执行,其self参数指向实例本身,可用于调用实例属性等如self.abc;在定义实例时,__init__以外的方法不会自动执行。(类在创建实例时的流程为①调用__new__方法(关于其他魔法方法后详),创建对象占用内存空间;②调用__init__方法,将刚刚创建的对象的引用传递给self即初始化)

其他实例方法:实例方法即在类中定义的不加特殊处理的函数对象,其必须在参数中添加self以代表实例本身(因为在类外无法通过Class.class()来访问无self参数的实例方法)。

静态方法:在python3的类中,允许存在不引用实例的方法(用于设置无需参数但属于类的函数),使用@staticmethod声明,可将无self参数的方法声明为静态方法,也可通过实例调用。

类方法:将类本身作为对象进行操作的方法,使用@classmethod定义(在类内),一般将类参数写为cls(同self),多用于操作类属性;类方法只能调用类变量,不能调用实例变量(类方法可以通过实例调用,但一般不这样使用)。
:关于实例方法、静态方法、类方法的区别:
①实例方法、类方法都必须传入一个self/cls参数(无论使用与否,都必须传入,否则无法调用),@classmethod意为该方法可以通过类来调用,即可以将类本身作为一个参数传入,即可以在类方法中处理类属性;
注意,通过实例调用类方法时,其并没有将实例作为对象传入,而是将实例所在的类作为对象传入,因此通过实例依然操作的是类方法和类属性,并不能通过实例调用类方法处理实例属性
②实例方法只用于处理实例,也只能通过实例来调用,其不能变动类属性,其self对象传入的是实例,不能传入类;
③静态方法对参数没有要求,其相当于在类中定义的全局函数,通过类和实例都可以调用,且因为对参数没有要求,所以也不涉及到类和实例的属性,比较佛系。

类属性与实例属性:类属性指定义在类中,并且在函数体外的属性,类属性可以在所有实例之间共享,实例属性首先继承类属性,并且在类中的方法中可以以self参数的形式定义,在类外需要以实例的方式定义,多个实例之间的实例属性不会相互影响。
:类属性与实例属性的概念很容易理解,但其中有一些需要注意的点:
①在类中的任意部分使用类名定义类属性或使用self定义实例属性,在类的所有地方都可以引用;
②在类中定义的是类属性,不是变量,不存在于命名空间,也不能直接引用,但可以在类中引用定义在类外的全局变量;
③方法定义时的默认参数与类属性平级,因此若需要设定默认参数为某个类属性,则不需使用Class.name调用类属性,且在定义方法时的参数为形式参数,其可与类属性重名(但不建议这么做,容易混淆);
④实例会首先继承其类属性作为实例属性,但可以对其进行重写,即当定义同名实例属性时覆盖;
⑤数据属性会覆盖同名的方法,不论是类属性或实例属性;
⑥每一个实例对象中都拥有一个__class__属性,用于记录创建它的类名,因此可以通过实例名.__class__.xxx修改类属性;
⑦类的属性不属于命名空间的搜索范畴,除了在设定类方法默认参数时可以直接引用,否则只能通过类/实例名进行引用和访问,注意实例产生时相当于将实例属性全部做了一次引用(目标为类属性),此后实例属性与类属性指向相同引用,若重新赋值实例属性,类属性不变,若类属性是可变变量,通过实例属性修改,则一起变化,类方法的上层命名空间即当前类所在的模块。 (注意实例有自己的属性,但没有自己的方法,在类外定义的实例/类方法,其实质为实例/类属性指向了一个函数对象)

访问限制:其对类的属性和方法通用,
①首尾双下划线表示魔法方法/属性,属于公有变量;
②单前置下划线表示保护(protect),python其实不支持保护设置,允许类/子类/实例进行访问,但是不能通过通配符*导入,报错变量名未定义,除此之外,其与普通变量在使用方法和性质上并无区别,只是属于一个通用的约定,程序并不受其影响;
③双前置下划线表示私有(private),python类其实不支持私有设置,其只是在类外重写变量名为_class__变量名,使用重写后的属性名可以在类外使用类/子类/实例进行访问(一般来说外部不需要引用的方法和变量全部定义为私有属性,外部需要引用的变量定义为public);
④单后置下划线,为了防止跟python中的关键字冲突。

继承与多态:①在OOP中,被继承的类称为父类/基类,新的类称为子类/派生类,子类继承父类的所有属性和方法(当继承多个父类时,按照MRO顺序最近继承属性);
②其定义方法与类的定义类似,但在类名后添加(BaseClass)以注明要继承的类;可在子类中重写同名方法,则子类的实例调用的方法为子类中重写后的方法;在创建子类的实例时,若子类没有重写__init__方法,则会自动调用基类此方法进行初始化,在重写后,不会再调用基类的__init__方法,若要调用,需使用super()函数;
③若在子类中进行方法重写后仍需调用基类的该方法,可以使用BaseClass.f(s)方式调用,其中s是子类的实例(在子类的方法中使用BaseClass.f(self)调用,其与上述类似,因为此时的self就指代一个实例);
④子类的实例都是父类的实例。

super函数:①是专为解决多重继承的问题存在的函数;在python2中的写法是super(cls, object).f(args),其中cls是mro队列起始值(不包含此类,从此类的父类开始),object是当前调用父类方法的实例对象,f为要调用的父类方法(若在当前类f中,cls与object可省略);
②若父类中也有super(),则统计所有含有super()的类(若无super()则中断继承树,当前类作为顶端),按广度优先的原则(一层一层查找)进行排序mro,按照顺序运行每一级的f();为了保证不出现命名混乱,所有类中的同名函数的形式参数名都必须保持一致。
:super在python3中的使用规则为①若要使用父类的方法,则可以使用简写super().f(),相当于传入了当前类作为类对象,当前实例作为实例对象,调用父类的f方法,注意调用父类的方法时无需添加self参数,因为此实例对象已经写在super()中了;
②若需要调用祖先类(不想调用直接父类),则使用super的完整写法super(cls, self).f(),其中cls是要调用的祖先类的前一个类(顺序以MRO为准),self为实例,也可以在类外调用,将self改为已经生成的实例对象即可;
③python会首先根据继承顺序生成MRO,然后根据此序列依次调用,当某个类中没有super函数时,中断,super函数与父类关系并无关联,其调用顺序只与MRO有关,super函数本身也不影响MRO
④一般是在重写方法时才会用到super,因此要求:Ⅰ父类中必须有同名该方法;Ⅱ子类与父类中的同名方法参数必须匹配;Ⅲ必须使用super调用父类方法。

MRO(Method Resolution Order):方法解析顺序,每一个类都有一个MRO,即合并所有父类的列表,其在类生成时就已经确定,遵循以下三条原则:①子类永远在父类前面;②如果有多个父类,则根据它们在MRO中的顺序被检查;③如果对下一个类存在两个合法的选择,选择第一个父类;python3中,MRO列表默认是通过C3线性算法(并不单纯是深度优先算法/广度优先算法)实现的。

C3线性算法:①基本公式:L(子类(父类1, 父类2)) = [子类] + merge(L(父类1), L(父类2) , [父类1, 父类2])
②merge操作:Ⅰ第一个列表的第一个元素是后续列表的第一个元素后续列表中没有再次出现,则将这个元素合并到最终的解析列表中,并从当前操作的所有列表中删除;
Ⅱ如果不符合,则跳过此元素,查找下一个列表的第一个元素,重复Ⅰ的判断规则;
Ⅲ如果最终无法把所有元素归并到解析列表, 则报错。
例如:标准菱形继承的MRO计算:

# L(A) = [A] + mearge(L(B), L(C), [B, C])
#      = [A] + mearge([B, D, object],[C, D, object], [B, C])
#      = [A, B] + mearge([D, object],[C, D, object], [C])
#      = [A, B, C] + mearge([D, object],[D, object])
#      = [A, B, C, D] + mearge([object],[object])
#      = [A, B, C, D, object] + mearge([],[])
#      = [A, B, C, D, object]

:可以使用ClassName.__mro__查看类的MRO序列。

类中的property方法:①作为装饰器使用,@property, @xx.setter, @xx.deleter,其分别表示只读可写可删除,其中后两者是前者的内置方法(因此必须先使用@property);@property方法意为将一个方法转换为属性(实际上是调用了get函数,@property的方法只能有一个self参数,不能有其他参数),@xx.setter方法用来设置属性可写并添加条件,@xx.deleter方法用来设置属性可删除,如图所示,可以直接通过s.score(S.socre不可)来对实例属性进行访问,并且可通过直接赋值修改,del s.score可删除。(注意三个@下的函数名一致,setter方法中可以修改值的范围进行界定)
在这里插入图片描述
:property的装饰器用法,其作用就是,修改对于实例属性赋值符号和del符号的操作,例如:可以将1.99通过property装饰器保存为199,但直接获取时将其/100,删除时将其值返回。

②当使用类属性的方式创建property属性时,property()方法有四个参数:
第一个参数是方法名,调用 对象.属性 时自动触发执行方法;
第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法;
第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法;
第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息;

class A:
	PRICE = property(get_price, set_price, del_price, '价格属性描述...')
	def get_price(self):
	...
	# 其中方法都是定义在类中的实例方法

a = A()
a.PRICE = 123 # 调用get_price方法

与上述装饰器的用法原理类似,可以设置实例属性名不等于方法名(但这个特定的属性名依然是固定的),另可以设置一个文档字符串用于描述信息。

鸭子类型与判断方法:①Duck typing 这个概念来源于美国印第安纳州的诗人詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-1916)的诗句:”When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.” 意大利软件工程师、Python软件基金会研究员Alex Martelli 于2000年左右在Python的邮件组中最早将这个概念引入了程序设计范畴中;
②鸭子类型是一种动态类型的风格,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定,即可表述为:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子;
③python中,不关心对象类型本身,而关心它是如何使用的,只要可以实现某个方法,则就可以对其进行相关的调用,例如只要一个对象实现了__iter__next方法,则它就可以被python解释器视为迭代器来使用;
④鸭子类型理念可能的不足:“鸭子类型”语言的程序可能会在运行时因为不具备某种特定的方法而抛出异常(例如一只学会了猫叫的狗,在需要集体上树时异常),其没有任何静态检查,如类型检查、属性检查、方法签名检查等;
dir()函数查看对象的属性(某些方法使用了运算符进行简化,如前述的__eq__方法),因为在python中一切皆对象,每个对象中都有一部分默认的和自定义的方法,但不会列出内置函数和变量,无参数时默认为当前模块;
isinstance(a,b)方法:判断一个对象是否是某个数据类型,其中a是对象,b是类型(int/list/Class等),不能检测函数对象;
issubclass(a,b)方法:判断a是否b的子类,其中a,b必须为类对象。

魔法属性与魔法方法:在python的类中,以双下划线开头结尾的属性与方法称为魔法属性/方法,其功能强大,且python解释器都给出了默认方法,因此除非需要修改其内部功能,否则不应对魔法方法/属性进行修改(魔法方法/属性都是针对于类的,然而在python中所有的对象都是类的实例或类对象,因此,所有的对象都具有魔法属性和魔法方法);
①以下是一些常用的魔法属性:
__doc__,表示类的描述信息,即文档字符串;
__dict__,类与类实例的该属性不同,Ⅰ类的__dict__属性存储了类定义的所有类属性、类方法等组成的键值对,但不包括继承而来的属性和方法;Ⅱ实例的__dict__属性存储了所有的实例属性的键值对,如果没有就为空;__init__方法其实就是对__dict__属性的初始化赋值;
__module__,该属性记录类定义的位置,如果定义的位置正好是主程序,那么该值为"_main_",否则是类属于的模块的名字;
__name__,是标识模块名的一个系统变量。分两种情况:Ⅰ假如当前模块是主模块(也就是调用其他模块的模块),那么此模块名字就是"__main__";假如此模块是被import的,则此模块名为文件名(不加后面的.py);
__class__,该属性指向该实例的类,即实例指向类对象,类对象指向元类;
__slots__,在定义类时添加语句__slots__ = ('a', 'b')限制类的实例所能添加的属性名(但不限制类添加的类属性),但对其子类不起作用;
__bases__:用于显示该类型的基类,object是所有类型的基类,包括type,同时type是所有类型的类型,包括object;
②一些常用的魔法方法:
__str__()/__repr__():可定义,用于在直接输出实例对象时自定义输出内容,返回值为str类型,如可定义在交互模式中实例的返回值为某个实例属性;
__getitem__/setitem/delitem():可以通过s['ddd']调用相关方法(即定义中括号记法的一些操作),定义获取设置删除操作对应的内容;
__getslice__/setslice/delslice:可以调用切片语法,即定义中括号切片的一些操作;
__call__(self):允许一个类的实例像函数一样被调用:x(a, b) 即相当于x.__call__(a, b),只作用于该类的实例;
__del__(self):当删除一个实例对象时,python解释器调用的方法;
__setattr__(self, name, value):用于拦截实例属性的赋值,s.name = xxx即调用了s.__setattr__(self, name, xxx),因此如果该函数下还有赋值语句如self.name = xxx,则会无限循环直至溢出,一般在拦截赋值时使用此种方式添加实例属性self.__dict__[key] = value
_getattr_(self, name):用于拦截实例属性的获取,但只有当属性name没有在实例的__dict__或它构造类的__dict__或基类的__dict__中没有找到,才会调用__getattr__,即当name属性可以通过正常机制查找到时,此方法不会被调用,与setattr类似的,不要在该函数下通过self.name访问属性,否则会无限循环;
__getattribute__(self, name):其作用与getattr类似,但无论属性name是否能通过正常机制查到,其都调用该方法,若同时定义了getattr与该方法,则只有在该方法失效、且正常机制查找不到时才会调用getattr方法;
__delattr__(self, name):与setattr类似,但必须在name属性有意义时(即属性名不违法,而非属性名必须存在)才进行拦截操作;
hasattr(obj, name):返回值为布尔值,判断对象中是否有该属性,不属于魔法方法;
globals():用字典的方式保存当前的所有对象,将变量名作为key,将对象作为value,包括内建函数。

:①其中__name__globals并不属于魔法属性/方法,但其性质类似,统一在这里进行说明;
②另有一些方法,用于处理运算符和内建函数的操作,如__len__方法即重定义len()函数;
③setattr/getattr等这些方法都存在一个非魔法方法直接调用的函数,其作用与上述类似,但两者的使用环境不同,前者用于在类中重定义,后者用于在类外添加/获取/删除类属性或实例属性(可直接使用符号操作代替);
__new____init__方法在后文元类中详述。

元类:用于创建类的类,实例对象是由类创建的,但类对象本身也是实例对象,将类对象作为实例创建的类就称为元类,在python中,这个创建所有类对象的类就是type。

类型与基类:①基类即继承关系,python3中所有类型(包括type)的基类都是object,这点可以通过__bases____mro__验证,所有类型的的mro的最后一个都是object;
②类型即创建者,例如整型1是由int类创建的,是int类的实例,就把int称为整型1的类型,所有类型的类型都是type,即上述,在python中,所有的类都是由type创建的,这点可以通过__class__来验证。

动态的创建:动态语言与静态语言最大的不同是,函数和类的定义,不是编译时定义的,而是运行时动态创建的,在运行到class/type时才会动态的去在内存空间中创建类,并赋给引用的变量名,所有class创建类的本质其实都是调用了type函数,而实例对象和函数是在调用时(而非运行到def时)才会在内存空间中创建,因此在函数中的语法错误等,若不调用是无法得知的。
:在函数中也是可以创建类的,可以将函数的返回值设置为类对象,但是这个类对象只存在于函数的局部命名空间中。

type函数:type函数保留了两种语义,①type(a)单参数形式,返回a的类型;②type(classname, (baseclassname, ~1, ~2), {xxx})其中第一个参数为类名(必须是字符串),第二个参数为继承的父类组成的元组(可以是父类名,也可以是指代父类的变量,不能是字符串),第三个参数为要在类中创建的对象与其名称的键值对组成的字典,其中可以引入类属性,f(self)作为实例方法,f(cls)作为类方法,@静态方法等(注意其中不仅有自定义的类属性,还有一部分默认的属性如__module____qualname__),如下,两种表述方式是相同的。

B = type('B', (A,), {'b':100})
class B(A):
	b = 100

:使用type创建的类,其返回值就是类对象,如果单纯使用type('B', (A,), {'b':100})创建了类而不使用某个变量去赋值B = type('B', (A,), {'b':100}),则在当前的命名空间中没有对于刚刚创建的这个类的引用,即虽然知道这个类的类名为B,但在当前的命名空间中没有对于B这个变量的定义,无法使用,因此一般将使用type创建的类赋值给类名相同的变量,class定义符则自动做了这个工作。

new与init方法__new__(cls, *args, **kwargs)方法用于控制类或实例对象的创建,其至少有一个参数cls,用于表示要实例化的类(注意这个参数与类中实例方法的self参数不同,其代表要实例化的类,而不是代表当前的实例对象或类对象,因此即使复用type的new方法,object的new方法,其都需要传入cls参数,这个参数会由python解释器自动写入为当前的类,但也可以手动写入为其他类,若写为其他类则创建出来的实例就是其他类的实例),new方法一般有三种用途:①用于metaclass创建类;②用于控制实例的创建(如可以给实例添加属性),因为实例的创建需要申请内存空间,所以对于初学者来讲,此处需要复用父类(object)或type的new方法,来进行实例的创建;③单例模式(后详);
__init__(self)方法必须有一个self参数用于指代当前的实例对象,其只是用于实例对象的初始化。

metaclass:直译为元类,可以用来控制类的创建行为,其主要目的就是要让新创建的类都满足一定的条件却无需再每一个类中都添加相关定义(如需要让某个模块中的所有类的属性名首字母大写)。
①普通的用class或type创建类时,其除了添加的类方法,其他方法都是默认存在的,在创建时会使用默认内置的type方法创建;
②当使用class创建类时,若声明了metaclass属性,则其会在自身/父类/模块级别查找metaclass属性,其不一定是一个类,也可以是一个方法,但必须返回一个类对象(使用type创建),当metaclass属性指向函数时如下所示:

def f(classname, classparent, classattr):
	newattr = {}
	print(classattr)
	for i,j in classattr.items():
		newattr[i+'b'] = j*2
	return type(classname, classparent, newattr)

class A(QWER, metaclass=f):
	a = 1
# A.ab == 2

若要使用metaclass拦截默认的type操作,在python3中在class定义符中声明;
③当metaclass属性指向一个类如A时,则必须重写A的__new__方法,其意为A创建的实例是一个类对象,注意此处虽然调用了A的new方法,但因为对其进行了重写,返回了type创建的类对象,因此创建出来的类的基类是object,类型是type,与A并没有关系,A只是作为一个工具的存在,而且因为重写了A的new方法,所以A本身已经无法再通过A()创建实例(因为其new方法已经只能返回一个类对象),除非使用object.__new__(A),即复用了object的new方法创建了A的实例。

class UpperAttrMetaClass(type): # **作为元类的类必须从type派生**,
    def __new__(cls, class_name, class_parents, class_attr):
        # 遍历属性字典,把不是__开头的属性名字变为大写
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法1:通过'type'来做类对象的创建,其__class__是type,注意type需要的参数是固定的,但是__new__方法需要的参数不是固定的,
        return type(class_name, class_parents, new_attr)

        # 方法2:复用type.__new__方法,此处传入的cls为当前类,因此创建出来的类对象是UpperAttrMetaClass类的实例,即其__class__是当前类
        # return type.__new__(cls, class_name, class_parents, new_attr)

class Foo(object, metaclass=UpperAttrMetaClass):
    bar = 'bip'

④当定义类时声明了metaclass,python就会依次在当前类、父类、模块中寻找指定的metaclass属性,若找到,则根据指定的函数或类中的new来创建,若找不到,则使用默认的type方法创建(为避免混乱,可视为当该类与该类的父类都没有在定义时设置metaclass属性时,按默认type方法处理);
作为元类的类必须从type派生

单例模式:每个实例都会占用一定的内存资源,且初始化实例时会影响运行性能,所以当整个系统只需一个实例时,使用单例模式不仅可减少资源占用,而且因为只需要初始化一次,还可以加快运行性能,单例模式还用于实现同步控制;其有四种实现方式:
①模块,首次导入模块时会生成.pyc文件,再导入时会直接加载pyc文件,其会校对原模块修改时间,若有变动则重新生成.pyc文件(即在模块中已经创建好的实例,在不修改模块源代码的情况下是不会重复创建的);
②使用__new__方法,在重写的new方法中引入一个私有属性(防止类外访问)用于存储实例,每次创建实例时都会判断这个私有属性是否为空,若不为空则返回已经创建的实例,如下所示;

class A:
	def __new__(cls, *args, **kwargs):
		if not hasattr(cls, '_instance'):
			cls._instance = super().__new__(cls, *args, **kwargs)
		return cls._instance

③使用装饰器,其思想与__new__方法类似,使用一个字典存储已创建的实例并判断,同时可以实现根据有无参数传入是否创建新的实例(可以装饰类的装饰器,其实只需要传入一个cls参数表示类对象即可);

def singleton(cls):
    instances = {}
    def getinstance(*args,**kwargs):
        if cls not in instances:
            instances[cls] = cls(*args,**kwargs)
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    a = 1

④使用元类,重写元类的call方法(其作用为控制类的实例创建),相对不常用,如下所示。

class Singleton2(type):
    def __init__(self, *args, **kwargs):
        self.__instance = None
        super(Singleton2,self).__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton2,self).__call__(*args, **kwargs)
        return self.__instance


class Foo(metaclass=Singleton2):
    pass

:此时作为元类的类并没有重写new方法,因此直接复用基类object的new方法,则创建的Foo其实是Singleton2的实例,因此在元类中重写call方法控制的就是作为Singleton2类实例的Foo的Foo()行为,此时的Foo()已经不能用于创建Foo类的实例了,其返回的是Singleton2类的实例。

ORM:Object Relational Mapping,对象关系映射,Django的核心思想,即将关系数据库的一行映射为一个对象,通过操作对象来操作数据库,将一个类作为一个表,通过类的实例来对数据库中的表进行操作。其基本步骤为:
①定义元类,在元类中将传入的参数处理,用类属性的方式存储表名和对应的字段名及字段限制(字段限制也可用专门定义的类来实现);
②定义基类,用元类的方法创建基类,则引入了元类中的类属性,并将类属性进行操作,定义一些合成SQL语句的方法;
③创建基类实例,即表的类对象,在定义类时继承基类,并且需要写入创建表所需的字段名/限制等,其会自动查找基类中的metaclass属性,用元类的方法创建,即其拥有在基类中创建的方法以及在元类中引入的类属性(一般是在基类中操作);
④创建基类实例的实例,即一条SQL语句,在创建这个实例时需要写入与字段名对应的值,调用继承的实例方法,完成SQL。
:①元类用于重写new方法(处理传入的类属性),基类用于保存实例方法,用户创建的模型类继承基类,因此通过元类创建,模型类的类属性即表的字段,模型类的实例属性即要添加的行,然后合成SQL语句;②ORM的主要目的即用户调用简单,无需写SQL语句;③创建实例时任意添加实例属性可以按下述方法处理:

class A:
	def __init__(self, **kw):
		for i,j in kw.items():
			self.__dict__[i] = j

小结:以上即python面向对象与类的相对全面的基础知识,只要理解了这部分内容,在此之上进行的扩充、应用等都是很容易的事。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值