Python是一个面向对象的语言,但是因为python语言的特性,我们大多的python程序员只是把它当做一个工具,在我们项目的实际使用中除了使用Django框架以外,其他的都是使用python最基础的方式,完全跟pythonic没有一点关系,当然主要还是因为我们能力太差。面向对象是书写各种框架的基础,python的面向对象不仅拥有通用面向对象的特性,而且还拥有一些极其强大的特性,可以让我们把它的便利性发挥到极致。本篇将详细介绍Python 类的成员、成员修饰符、类的特殊成员。
类的成员
类的成员可以分为三个方面:字段、方法和属性。
在定义一个类以后,方法、属性和静态字段都是属于类的,在内存中只保存一份,只有普通字段是属于类对象的,每个类对象都会创建并保存一份。
我们可以将存储区分为几种:程序存储区、全局存储区、静态存储区、栈存储区。
程序存储区: 我们所有的普通函数、类函数、类等程序所存储的位置。
全局存储区:所有全局变量的存储区。
静态存储区:类的静态变量存储区。
栈存储区:栈存储区就是局部变量存储,所有的局部变量、类对象等都存储在这里。
字段
字段包括:普通字段和静态字段,使用和定义都是不一样的,其最本质的区别就是内存中保存的位置不同。
普通字段属于对象
静态字段属于类
classDemo: aa="我是静态字段" def__init__(self,name): self.bb=namedefcustom_func(self,name): self.cc=name
obj1=Demo(11) Demo.aa=111111obj1.aa=1obj1.custom_func(1111) obj2=Demo(22) obj2.aa=2Demo.aa=222222obj2.custom_func(2222) print("obj1.aa=",obj1.aa) print("obj1.bb=",obj1.bb) print("obj1.cc=",obj1.cc) print("Demo.aa=",Demo.aa) print("obj2.aa=",obj2.aa) print("obj2.bb=",obj2.bb) print("obj2.cc=",obj2.cc) print("Demo.aa=",Demo.aa) print("id(Demo.aa)=",id(Demo.aa)) print("id(obj1.aa)=",id(obj1.aa)) print("id(obj2.aa)=",id(obj2.aa))
输出
obj1.aa=1 obj1.bb=11 obj1.cc=1111 Demo.aa=222222 obj2.aa=2 obj2.bb=22 obj2.cc=2222 Demo.aa=222222 id(Demo.aa)=4645988944 id(obj1.aa)=4530158768 id(obj2.aa)=4530158800
从上面我们可以看出,bb、cc为普通字段,每个类独有一份,相互不影响,而且需要使用类对象访问,而Demo.aa和obj.aa是不同的,我们看到obj.aa的行为与普通字段类似,如果去掉obj1.aa=xx和obj2.aa=xx,那么obj1.aa和Demo.aa是相同的,在内存中的地址也是相同的。这里涉及到python类的另外一种特性-临时属性。
通常情况下我们都使用普通字段,当一个变量在类的所有对象中共同使用,而且数据共享的时候,我们就可以使用静态字段。
方法
方法包括:普通方法、静态方法和类方法。他们的区别在于调用方式不同。
普通方法:由对象调用;包含一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
类方法:由类调用; 包含一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
静态方法:由类调用;没有默认参数;
classFoo: FooName="静态字段" defcustom_func(self,name): #普通方法,至少包含一个self参数 print("普通方法")@classmethod defclass_func(cls,name): #类方法,至少包含一个cls参数 print("类方法")@staticmethod defstatic_func(name): #静态方法,没有默认参数 print("静态方法") f=Foo()#调用普通方法f.custom_func("aaa")#调用类方法Foo.class_func("bbb")#调用静态方法Foo.static_func("ccc")
普通方法中可以直接使用对象的普通字段self.name
类方法中可以直接使用静态字段cls.FooName
静态方法中不能直接使用普通字段和静态字段
注意:静态字段Foo.FooName可以在任意地方调用,包括以上三种场景内,如果在普通方法中也可以使用 self.FooName调用,前提是对象没有同名的普通字段
属性
上面我们已经介绍了Python类中的方法,那么属性就非常简单了,因为Python中的属性其实是普通方法的变种。
对于属性,有以下两个知识点:
基本使用
两种定义方式
classFoo: deffunc(self): pass@property defprop(self): print("属性") f=Foo()#调用函数f.func()#调用属性f.prop
通过上面的例子,我们知道属性有以下几点:
普通方法上添加@property装饰器
有且只能有一个self参数,不能额外增加参数
调用时不需要括号
我们在做网页数据展示的时候,页面上显示数据的数据,不可能一次性把数据库中的所有内容都显示在页面上,一般都是通过分页功能实现,所以每次请求都会根据当前页数current_page和每页显示数量page_count来取出指定范围的数据
classPager: def__init__(self,current_page): #当前显示的页码 self.current_page=current_page#每页默认显示数据 self.per_items=10@property defstart(self): return(self.current_page-1)*self.per_items@property defend(self): returnself.current_page*self.per_items pages=Pager(1) pages.start#起始值pages.end#结束值
属性的两种定义方式
属性的定义有两种方式:
装饰器:在方法上应用装饰器
静态字段:在类中定义值为property对象的静态字段
我们知道Python中的类有经典类和新式类之分,如果类继承自object,那么该类是新式类,新式类的属性比经典类更丰富。但是现在都已经使用python3了,而python3中默认类都继承自object,所以python3中全是新式类。
1. 装饰器方式:普通方法加上@property装饰器
classGoods:@property defprice(self): print("@property")@price.setter defprice(self,val): print("@price.setter:",val)@price.deleter defprice(self): print("@price.deleter") obj=Goods() obj.price#自动执行@property修饰的price方法,并获取方法的返回值obj.price=100 #自动执行@price.setter修饰的price方法,并将100赋值给方法的参数delobj.price #自动执行@price.deleter修饰的price方法
2. 静态字段方式,使用property创建静态字段
property是一个类,在builtins.py文件中,初始化函数:def __init__(self, fget=None, fset=None, fdel=None, doc=None),有四个参数
第一个参数fget是方法名,调用 对象.属性 时自动触发执行方法
第二个参数fset是方法名,调用 对象.属性 = XXX 时自动触发执行方法
第三个参数fdel是方法名,调用 del 对象.属性 时自动触发执行方法
第四个参数doc是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息
classFoo: def__init__(self): self.price=10 defget_price(self): returnself.price#set函数必须有两个参数 defset_price(self,value): self.price=valuedefdel_price(self): delself.price PRICE=property(get_price,set_price,del_price,"descriptionPrice") f=Foo() print(f.PRICE)#自动调用get_pricef.PRICE=20 #自动调用set_priceprint(f.PRICE) #自动调用del_pricedelf.PRICE #自动调用descriptionPrice