类的成员
类的成员可以分为三大类 : 字段 , 方法和属性
类成员 :
字段 : 普通字段 静态字段
方法 : 普通方法 静态方法 类方法
属性 : 普通属性
注 : 所有成员中 , 只有普通字段的内容保存对象中 , 即 : 根据此类创建了多少对象 , 在内存中就有多少个普通字段 . 而其他的成员 , 则都是保存在类中 , 即 : 无论对象的多少 , 在内存中只创建一个 .
一 . 变量
变量包括 : 实例变量(字段)和类变量(静态字段)
* 实例变量(字段)属于对象
* 类变量(静态字段)属于类
class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name # 直接访问普通字段 obj = Province('河北省') print obj.name # 直接访问静态字段 Province.country 字段的定义和使用
由上述代码可以看出[普通字段需要通过对象来访问] [静态字段通过类访问]
* 静态字段在内存中只保存一份
* 普通字段在每个对象中都要保存一份
class Foo: # 类变量(静态字段) country = "中国" def __init__(self,name): # 实例变量(字段) self.name = name def func(self): pass obj1 = Foo('小桃红') obj2 = Foo('何润东') Foo.country
# 无法访问 : class Base(object): __secret = "受贿" class Foo(Base): def func(self): print(self.__secret) print(Foo.__secret) obj = Foo() obj.func() # 可以访问 class Base(object): __secret = "受贿" def zt(self): print(Base.__secret) class Foo(Base): def func(self): print(self.__secret) print(Foo.__secret) obj = Foo() obj.zt()
二 . 方法
方法包括 : 普通方法 , 静态方法和类方法 , 三种方法在内存中都归属类 , 区别在于调用方式不同 .
* 实例方法 : 由对象调用 ; 至少一个self参数 ; 执行普通方法时 , 自动将调用该方法的对象赋值给self ;
class Foo(object): def __init__(self, name): self.name = name # 实例方法 def func(self): print(self.name) obj = Foo('..') obj.func()
* 类方法 : 由类调用 ; 至少一个cls参数 ; 执行类方法时 , 自动将调用该方法的类复制给cls ;
class Foo(object): def __init__(self, name): self.name = name # 静态方法,如果方法无需使用对象中封装的值,那么就可以使用静态方法 @staticmethod def display(a1,a2): return a1 + a2 Foo.display(1,3)
* 静态方法 : 由类调用 ; 无默认参数;
class Foo(object): # 类方法,cls是类 @classmethod def show(cls,x1,x2): print(cls,x1,x2) # 执行类方法 Foo.show(1,8)
class Foo: def __init__(self, name): self.name = name def ord_func(self): """ 定义普通方法,至少有一个self参数 """ # print self.name print '普通方法' @classmethod def class_func(cls): """ 定义类方法,至少有一个cls参数 """ print '类方法' @staticmethod def static_func(): """ 定义静态方法 ,无默认参数""" print '静态方法' # 调用普通方法 f = Foo() f.ord_func() # 调用类方法 Foo.class_func() # 调用静态方法 Foo.static_func() 方法的定义和使用
相同点 : 对于所有的方法而言 , 均属于类中 , 所以 , 在内存也只保存一份 .
不同点 : 方法调用者不同 , 调用方法时自动传入的参数不同 .
三 . 属性
属性就是实例方法的变种 .
属性的知识点 :
* 属性的基本使用
* 实现的两种定义方式
1 . 属性的基本使用
# ############### 定义 ############### class Foo: def func(self): pass # 定义属性 @property def prop(self): pass # ############### 调用 ############### foo_obj = Foo() foo_obj.func() foo_obj.prop #调用属性
由属性的定义和调用要注意以下几点 :
* 定义时 , 在普通方法的基础上面添加 @property 装饰器 ;
* 定义时 , 属性仅有一个 self 参数
* 调用时 , 无需括号
方法 : foo_obj.func()
属性 : foo_obj.prop
注意 : 属性存在意义是 : 访问属性时可以制造出和访问字段完全相同的假象
属性由方法变种而来 , 如果Python中没有属性 , 方法完全可以代替其功能 .
# 根据用户请求的当前页和总数据条数计算出 m 和 n # 根据m 和 n 去数据库中请求数据 # ############### 定义 ############### class Pager: def __init__(self, current_page): # 用户当前请求的页码(第一页、第二页...) self.current_page = current_page # 每页默认显示10条数据 self.per_items = 10 @property def start(self): val = (self.current_page - 1) * self.per_items return val @property def end(self): val = self.current_page * self.per_items return val # ############### 调用 ############### p = Pager(1) # p.start 就是起始值,即:m # p.end 就是结束值,即:n
由上述可见 , Python的属性的功能是 : 属性内部进行一系列的逻辑计算 , 最终将计算结果返回.
2 . 属性的两种定义方式
属性的定义有两种方式 :
装饰器 即 : 在方法上应用装饰器
静态字段 即 : 在类中定义值为property对象的静态字段
装饰器方式 : 在类的普通方法上应用@property装饰器
经典类 :
# ############### 定义 ############### class Goods: @property def price(self): return "wupeiqi" # ############### 调用 ############### obj = Goods() result = obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值
新式类 :
# ############### 定义 ############### class Goods(object): @property def price(self): print '@property' @price.setter def price(self, value): print '@price.setter' @price.deleter def price(self): print '@price.deleter' # ############### 调用 ############### obj = Goods() obj.price # 自动执行 @property 修饰的 price 方法,并获取方法的返回值 obj.price = 123 # 自动执行 @price.setter 修饰的 price 方法,并将 123 赋值给方法的参数 del obj.price # 自动执行 @price.deleter 修饰的 price 方法
注:经典类中的属性只有一种访问方式,其对应被 @property 修饰的方法
新式类中的属性有三种访问方式,并分别对应了三个被@property、@方法名.setter、@方法名.deleter修饰的方法
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
class Goods(object): def __init__(self): # 原价 self.original_price = 100 # 折扣 self.discount = 0.8 @property def price(self): # 实际价格 = 原价 * 折扣 new_price = self.original_price * self.discount return new_price @price.setter def price(self, value): self.original_price = value @price.deltter def price(self, value): del self.original_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 del obj.price # 删除商品原价 实例
静态字段方式 ,创建之为property对象的静态字段
当使用静态字段的方式创建属性时 , 经典类和新式类无区别
property的构造方法中有四个参数:
1 . 方法名 , 调用 对象.属性 时自动触发执行方法
2 . 方法名 , 调用 对象.属性 = xxx 时自动触发执行方法
3 . 方法名 , 调用 del 对象.属性 时自动触发执行方法.
4 . 字符串 , 调用 对象.属性.__doc__ ,次参数是该属性的描述信息
class Foo: def get_bar(self): return 'wupeiqi' # *必须两个参数 def set_bar(self, value): return return 'set value' + value def del_bar(self): return 'wupeiqi' BAR = property(get_bar, set_bar, del_bar, 'description...') obj = Foo() obj.BAR # 自动调用第一个参数中定义的方法:get_bar obj.BAR = "alex" # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入 del Foo.BAR # 自动调用第三个参数中定义的方法:del_bar方法 obj.BAE.__doc__ # 自动获取第四个参数中设置的值:description...
类成员的修饰符
对于每一个类的成员而言都有两种形式 :
* 公有成员 , 在任何地方都能访问
* 私有成员 , 只有在类的内部才能访问
class C: def __init__(self): self.name = '公有字段' self.__foo = "私有字段"
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
class C: name = "公有静态字段" def func(self): print C.name class D(C): def show(self): print C.name C.name # 类访问 obj = C() obj.func() # 类内部可以访问 obj_son = D() obj_son.show() # 派生类中可以访问 公有静态字段
class C: __name = "私有静态字段" def func(self): print C.__name class D(C): def show(self): print C.__name C.__name # 类访问 ==> 错误 obj = C() obj.func() # 类内部可以访问 ==> 正确 obj_son = D() obj_son.show() # 派生类中可以访问 ==> 错误 私有静态字段
普通字段
- 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访问;
class C: def __init__(self): self.foo = "公有字段" def func(self): print self.foo # 类内部访问 class D(C): def show(self): print self.foo # 派生类中访问 obj = C() obj.foo # 通过对象访问 obj.func() # 类内部访问 obj_son = D(); obj_son.show() # 派生类中访问 公有字段
class C: def __init__(self): self.__foo = "私有字段" def func(self): print self.foo # 类内部访问 class D(C): def show(self): print self.foo # 派生类中访问 obj = C() obj.__foo # 通过对象访问 ==> 错误 obj.func() # 类内部访问 ==> 正确 obj_son = D(); obj_son.show() # 派生类中访问 ==> 错误 私有字段
私有成员只能在类内部使用
类的组合
类或对象可以做字典中的key和value的
lass Foo: pass user_info = { Foo:1, Foo():5 } print(user_info)
让我们看一看对象中到底有什么?
class StarkConfig(object): def __init__(self,num): self.num = num def changelist(self,request): print(self.num,request) class RoleConfig(StarkConfig): def changelist(self,request): print('666') # 创建了一个列表,列表中有三个对象(实例) # [ StarkConfig对象(num=1), StarkConfig对象(num=2), RoleConfig对象(num=3) ] config_obj_list = [StarkConfig(1),StarkConfig(2),RoleConfig(3)] for item in config_obj_list: print(item.num) # 结果 # 1 # 2 # 3
class UserInfo(object): pass class Department(object): pass class StarkConfig(object): def __init__(self,num): self.num = num def changelist(self,request): print(self.num,request) def run(self): self.changelist(999) class RoleConfig(StarkConfig): def changelist(self,request): print(666,self.num) class AdminSite(object): def __init__(self): self._registry = {} def register(self,k,v): self._registry[k] = v(k) site = AdminSite() site.register(UserInfo,StarkConfig) site.register(Department,StarkConfig) print(len(site._registry)) # 3 for k,row in site._registry.items(): row.run()
总结 :
1. 对象中封装了什么?
2. self到底是谁?
只有明白这两个问题,就不会错
# ############# 创建学校 ########## class School: def __init__(self, address): self.address = address bj = School('北京校区') sh = School('上海校区') sz = School('深圳校区') # ################ 创建课程 ########## class Course(object): def __init__(self, name, period, price, school=None): self.name = name self.period = period self.price = price self.school = school # ########## 学校和课程的组合 ######### py1 = Course('Python全栈', 110, 19999, bj) py2 = Course('Python全栈', 110, 19999, sh) py3 = Course('Python全栈', 110, 19999, sz) l1 = Course('Linux运维', 110, 19999, bj) l2 = Course('Linux运维', 110, 19999, sh) g1 = Course('Go开发', 119, 19999, bj) class Grade(object): def __init__(self, name, people, introduce, course=None): self.name = name self.people = people self.introduce = introduce self.course = course # ######### 班级和课程的组合 ######### # 北京 gr1 = Grade('11期',20,'....',py1) gr2 = Grade('15期',20,'....',py1) gr3 = Grade('11期',20,'....',l1) gr4 = Grade('12期',20,'....',l1) gr5 = Grade('21期',20,'....',g1) gr6 = Grade('65期',20,'....',g1) # 上海 gr7 = Grade('1期',20,'....',l2) gr8 = Grade('2期',20,'....',l2) gr9 = Grade('1期',20,'....',py2) gr10 = Grade('2期',20,'....',py2) # 深圳 gr11 = Grade('1期',20,'....',py3) # 查看哪个学校哪个课程哪个班级人数 print(gr1.people) # 查看哪个学校哪个课程的学费 print(gr1.course.price) # 查看哪个课程哪个班级在哪个学校 print(gr1.course.school.address)
1 . 主动调用其他类的成员
class Base(object): def f1(self): print('5个功能') class Foo(object): def f1(self): print('3个功能') Base.f1(self) #调用别的类 obj = Foo() obj.f1()
class Foo(object): def f1(self): super().f1() #按照类的继承顺序找下一个 print('3个功能') class Bar(object): def f1(self): print('6个功能') class Info(Foo,Bar): pass obj = Info() obj.f1() # 结果: ''' 6个功能 3个功能 '''
类的特殊成员
上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:
1. __init__
初始化方法 , 对空对象初始化 , [类名()] 自动触发执行
class Foo(object): def __init__(self,a1,a2): self.a1 = a1 self.a2 = a2 obj = Foo(1,2)
2. __call__
对象后面加括号,触发执行 对象()
class Foo(object): def __init__(self,a1,a2): self.a1 = a1 self.a2 = a2 def __call__(self, *args, **kwargs): print(11111,args,kwargs) return 123 ret = obj(6,4,2,k1=456)
3. __getitem__
用于索引操作 , 自动触发执行 对象["xx"]
class Foo(object): def __init__(self,a1,a2): self.a1 = a1 self.a2 = a2 def __getitem__(self, item): print(item) return 8 ret = obj['yu'] print(ret)
4. __setitem__
用于索引操作 , 自动触发执行 对象["xx"] = 任意
class Foo(object): def __init__(self,a1,a2): self.a1 = a1 self.a2 = a2 def __setitem__(self, key, value): print(key,value,111111111) obj['k1'] = 123
5. __delitem__
析构方法 , 当对象在内存中被释放时 , 自动触发执行 del 对象[xx]
class Foo(object): def __init__(self,a1,a2): self.a1 = a1 self.a2 = a2 def __delitem__(self, key): print(key) obj = Foo(1,2) del obj['uuu']
6. __add__
对象 + 对象 自动触发执行
class Foo(object): def __init__(self,a1,a2): self.a1 = a1 self.a2 = a2 def __add__(self, other): return self.a1 + other.a2 obj1 = Foo(1,2) obj2 = Foo(88,99) ret = obj2 + obj1 print(ret)
7. __enter__ / __exit__
用于文件操作 , 自动触发执行 with 对象
class Foo(object): def __init__(self,a1,a2): self.a1 = a1 self.a2 = a2 def __enter__(self): print('1111') return 999 def __exit__(self, exc_type, exc_val, exc_tb): print('22222') obj = Foo(1,2) with obj as f: print(f) print('内部代码')
8. __new__
真正的构造方法
class Foo(object): def __init__(self, a1, a2): # 初始化方法 """ 为空对象进行数据初始化 :param a1: :param a2: """ self.a1 = a1 self.a2 = a2 def __new__(cls, *args, **kwargs): # 构造方法 """ 创建一个空对象 :param args: :param kwargs: :return: """ return object.__new__(cls) # Python内部创建一个当前类的对象(初创时内部是空的.).
9. __str__
class Foo(object): def __init__(self): pass def func(self): pass def __str__(self): return "F1" obj = Foo() print(obj,type(obj)) """ 结果: F1 类Foo """
10. __doc__
class Foo(object): """ asdfasdfasdfasdf """ def __init__(self): pass def func(self): pass def __str__(self): return "F1" obj = Foo() print(obj.__doc__) """ 结果: asdfasdfasdfasdf """
11. __dict__
class Foo(object): def __init__(self,name,age): self.name = name self.age = age def func(self): pass obj1 = Foo('太白',99) obj2 = Foo('彦涛',89) print(obj1.__dict__) print(obj2.__dict__) """ 结果: {'name': '太白', 'age': 99} {'name': '彦涛', 'age': 89} """
12. __iter__
# l1是list类的一个对象,可迭代对象 l1 = [11,22,33,44] # l2是list类的一个对象,可迭代对象 l2 = [1,22,3,44] class Foo(object): def __init__(self,name,age): self.name = name self.age = age def func(self): pass def __iter__(self): # return iter([11,22,33,44,55,66]) yield 11 yield 22 yield 33 # obj1是Foo类的一个对象,可迭代对象 """ 如果想要把不可迭代对象 -> 可迭代对象 1. 在类中定义__iter__方法 2. iter内部返回一个迭代器(生成器也是一种特殊迭代器) """ obj1 = Foo('刘博文',99) for item in obj1: print(item)
经典题型:
class StarkConfig(object): list_display = [] def get_list_display(self): self.list_display.insert(0,33) return self.list_display class RoleConfig(StarkConfig): list_display = [11,22] s1 = RoleConfig() s2 = RoleConfig() result1 = s1.get_list_display() print(result1) result2 = s2.get_list_display() print(result2) """ 结果: [33,11,22] [33,33,11,22] """
反射
1. issubclass/type/isinstance
a. issubclass
class Base(object): pass class Foo(Base): pass class Bar(Foo): pass print(issubclass(Bar,Base)) # 检查第一个参数是否是第二个参数的 子子孙孙类
b. type
获取当前对象是由哪个类创建
class Foo(object): pass obj = Foo() print(obj,type(obj)) if type(obj) == Foo: print('obj是Foo类型')
class Foo(object): pass class Bar(object): pass def func(*args): foo_counter =0 bar_counter =0 for item in args: if type(item) == Foo: foo_counter += 1 elif type(item) == Bar: bar_counter += 1 return foo_counter,bar_counter # result = func(Foo(),Bar(),Foo()) # print(result) v1,v2 = func(Foo(),Bar(),Foo()) print(v1,v2)
c. isinstance
isinstance(xx.xx)检查第一个参数(对象)是否由第二个参数(派生类及基类)的实例.
class Base(object): pass class Foo(Base): pass obj1 = Foo() print(isinstance(obj1,Foo)) # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。 print(isinstance(obj1,Base)) # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。 obj2 = Base() print(isinstance(obj2,Foo)) # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。 print(isinstance(obj2,Base)) # 检查第一个参数(对象)是否是第二个参数(类及父类)的实例。
2. 方法和函数
对象.xxx ----> xxx就是方法
类.xxx ----> xxx就是函数
xxx ----> xxx就是函数
打印查看:
function 函数
method 方法
代码检查:
from types import MethodType,FunctionType def check(arg): """ 检查arg是方法还是函数? :param arg: :return: """ if isinstance(arg,MethodType): print('arg是一个方法') elif isinstance(arg,FunctionType): print('arg是一个函数') else: print('不知道是什么')
3. 反射
getattr # 根据字符串的形式 , 去对象中找成员
import xx v1 = getattr(xx,'x1') # xx是一个模块 v2 = getattr(xx,'f1') v2('杨森')
hasattr # 根据字符串的形式 , 去判断对象中是否由成员
v3 = hasattr(xx,'x1') v4 = hasattr(xx,'f1') v4 = hasattr(xx,'f1') v5 = hasattr(xx,'xxxxxxx') print(v3,v4,v5)
setattr # 根据字符串的形式 , 动态的设置一个成员(在内存中)
setattr(xx,'x2',999) v6 = getattr(xx,'x2') print(v6)
setattr(xx,'f2',lambda x:x+1) v7 = getattr(xx,'f2') v8 = v7(1) print(v8)
delattr # 根据字符串的形式 , 动态的删除一个成员(在内存中)
delattr(xx,'x1') v9 = getattr(xx,'x1') print(v9)
总结 : v = getattr(obj,"函数名或方法名")
根据字符串为参数(第二个参数) , 去对象(第一个参数)中寻找与之同名的成员.
from types import FunctionType import handler while True: print(""" 系统支持的函数有: 1. f1 2. f2 3. f3 4. f4 5. f5 """) val = input("请输入要执行的函数:") if hasattr(handler,val): func_or_val = getattr(handler,val) # 根据字符串为参数,去模块中寻找与之同名的成员。 if isinstance(func_or_val,FunctionType): func_or_val() else: print(func_or_val) else: print('handler中不存在输入的属性名')
class Foo(object): def __init__(self,a1): self.a1 = a1 self.a2 = None obj = Foo(1) v1 = getattr(obj,'a1') print(v1) setattr(obj,'a2',2) v2 = getattr(obj,'a2') print(v2)
class Account(object): func_list = ['login', 'logout', 'register'] def login(self): """ 登录 :return: """ print('登录111') def logout(self): """ 注销 :return: """ print('注销111') def register(self): """ 注册 :return: """ print('注册111') def run(self): """ 主代码 :return: """ print(""" 请输入要执行的功能: 1. 登录 2. 注销 3. 注册 """) choice = int(input('请输入要执行的序号:')) func_name = Account.func_list[choice-1] # func = getattr(Account,func_name) # Account.login # func(self) func = getattr(self, func_name) # self.login func() obj1 = Account() obj1.run() obj2 = Account() obj2.run()
4. 判断对象是否可以被调用
def func(): pass class Foo(object): def __call__(self, *args, **kwargs): pass def func(self): pass obj = Foo() print(callable(func)) print(callable(Foo)) print(callable(obj))