一面向对象的结构和成员
1.1面向对象的结构
class A: company_name = '老男孩教育' # 静态变量(静态字段) __iphone = '1353333xxxx' # 私有静态变量(私有静态字段) def __init__(self,name,age): #普通方法(构造方法) self.name = name #对象属性(普通字段) self.__age = age # 私有对象属性(私有普通字段) def func1(self): # 普通方法 pass def __func(self): #私有方法 print(666) @classmethod # 类方法 def class_func(cls): """ 定义类方法,至少有一个cls参数 """ print('类方法') @staticmethod #静态方法 def static_func(): """ 定义静态方法 ,无默认参数""" print('静态方法') @property # 属性 def prop(self): pass
1.2面向对象的私有和公有
对于静态字段(静态变量),普通字段(对象属性),方法
公有:类可以访问,类内部可以访问,派生类可以访问
私有:仅类内部可以访问
class C: name = "公有静态字段" def func(self): print C.nameclass D(C): def show(self): print C.nameC.name # 类访问obj = C()obj.func() # 类内部可以访问obj_son = D()obj_son.show() # 派生类中可以访问
class C: __name = "私有静态字段" def func(self): print C.__nameclass D(C): def show(self): print C.__nameC.__name # 不可在外部访问obj = C()obj.__name # 不可在外部访问obj.func() # 类内部可以访问 obj_son = D()obj_son.show() #不可在派生类中可以访问
class C: __name = "私有静态字段" def func(self): print C.__nameclass D(C): def show(self): print C.__nameC.__name # 不可在外部访问obj = C()obj.__name # 不可在外部访问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): pass def add(self): print('in C')class D(C): def show(self): print('in D') def func(self): self.show()obj = D()obj.show() # 通过对象访问 obj.func() # 类内部访问 obj.add() # 派生类中访问
class C: def __init__(self): pass def __add(self): print('in C')class D(C): def __show(self): print('in D') def func(self): self.__show()obj = D()obj.__show() # 通过不能对象访问obj.func() # 类内部可以访问obj.__add() # 派生类中不能访问
1.3面向对象的成员
1 字段
class Province: # 静态字段 country = '中国' def __init__(self, name): # 普通字段 self.name = name# 直接访问普通字段obj = Province('河北省')print obj.name# 直接访问静态字段Province.country
上面的代码可以看出:普通字段需要通过对象来访问 , 静态字段需要通过类访问
内存中存储方式:
静态字段在内存中子保存一份,实例化对象后,访问静态字段都指向类中的这个内存地址
普通字段在每个对象中都要保存一份
2方法
方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。
- 普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;
- 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类复制给cls;
- 静态方法:由类调用;无默认参数;
class Foo: def __init__(self, name): self.name = name def ord_func(self): """ 定义普通方法,至少有一个self参数 """ # print self.name print '普通方法' @classmethod def class_func(cls): """ 定义类方法,至少有一个cls参数 """ return cls('xingchen') @staticmethod def static_func(): #设置不依赖于类和函数,不需要它们传self或者cls """ 定义静态方法 ,无默认参数""" print '静态方法' @staticmethod def static_func2(n): #可以设置为传递一个参数 print('n') # 调用普通方法f = Foo()f.ord_func()# 调用类方法Foo.class_func()Foo.name #可以打印出xingchen# 调用静态方法Foo.static_func() f.static_func2(1) Foo.static_func2(2)
相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。
不同点:方法调用者不同、调用方法时自动传入的参数不同。
3属性
什么是property
它是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
class People: def __init__(self,name,weight,height): self.name=name self.weight=weight self.height=height @property def bmi(self): return self.weight / (self.height**2)p1=People('egon',75,1.85)print(p1.bmi) #本来应该时p1.bmi()
为什么要用property
将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
由于新式类中具有三种访问方式,我们可以根据他们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除
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_priceobj = Goods()obj.price # 获取商品价格obj.price = 200 # 修改商品原价del obj.price # 删除商品原价
二面向对象的内置函数
1 isinstance和issubclass
isinstance() 与 type() 区别:
type() 不会认为子类是一种父类类型,不考虑继承关系。 isinstance() 会认为子类是一种父类类型,考虑继承关系。#isinstance(obj,cls)检查是否obj是否是类 cls 的对象class A:passclass B(A):passabj=B()print(isinstance(abj,B)) #Trueprint(isinstance(abj,A)) #True#issubclass(sub, super)检查sub类是否是 super 类的派生类print(issubclass(B,A)) #True
2 反射
反射:是通过字符串的形式去操作对象相关的属性
I对实例化对象的反射
class Foo: f='类的静态变量' def __init__(self,name,age): self.name=name self.age=age def say_hi(self): print('my name is: ',self.name)obj=Foo('xincheng',18)print(obj.name) #xinchengprint(hasattr(obj,'name')) #True 检查是否含有name的属性#获取my_name=getattr(obj,'name') #获取某个对象的属性print(my_name) #xinchengfunc=getattr(obj,'say_hi') #获取某个对象的属性func() #my name is: xincheng#报错设置# print(getattr(obj,'aa')) #报错:AttributeError: 'Foo' object has no attribute 'aa'print(getattr(obj,'aa','没有此属性')) #可以设置自己需要的报错说明 没有此属性#增加或修改setattr(obj,'sb','hehe')print(obj.__dict__) #{'name': 'xincheng', 'age': 73, 'sb': 'hehe'}print(obj.sb) #hehesetattr(obj,'show_name',lambda self:self.name+'sb') #此属性相当于设置了一个函数print(obj.__dict__) #{'name': 'xincheng', 'age': 73, 'sb': 'hehe', 'show_name':at 0x0000027089CA6048>}print(obj.show_name(obj)) #xinchengsb#删除delattr(obj,'age')delattr(obj,'show_name')delattr(obj,'show') #没有此属性报错,不能设置报错说明
II对类的反射
class Foo(object): staticField = "old boy" def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' @staticmethod def bar(): return 'bar'print(getattr(Foo, 'staticField'))print(getattr(Foo, 'func'))print(getattr(Foo, 'bar'))#结果为:# old boy## func=getattr(Foo,'bar')print(func) ## 内存地址和类的bar方法一样
III对当前模块
def login(): print(888)msg=input('>>>>').strip()import sysprint(sys.modules)this_module=sys.modules[__name__]print(this_module) #print(hasattr(this_module,'login')) #Trueif hasattr(this_module,msg): getattr(this_module,msg)()else: print('没有此属性')
IIII对另外一个模块
#模块名称模块测试反射,内容如下def test(): print('来自于另外一个模块')#本地操作:import 模块测试反射 as objprint(hasattr(obj,'test'))getattr(obj,'test')()
3 __len__
class A: def __init__(self): self.a = 1 self.b = 2 def __len__(self): return len(self.__dict__)a = A()print(a.__dict__) #{'a': 1, 'b': 2}print(len(a)) #2 使用len函数的时候它计算的是实例化对象字典的长度
4 __hash__
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a)+str(self.b))a = A()print(hash(a)) #这个hash值刷新一次改变一次
5 __str__和__repr__
如果一个类定义了__str__方法,那么在打印的时候,默认触发该方法
format_dict={ 'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型 'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址 'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名}class School: def __init__(self,name,addr,type): self.name=name self.addr=addr self.type=type def __repr__(self): return 'School(%s,%s)' %(self.name,self.addr) def __str__(self): return '(%s,%s)' %(self.name,self.addr) def __format__(self, format_spec): if not format_spec or format_spec not in format_dict: format_spec='nat' fmt=format_dict[format_spec] return fmt.format(obj=self)s1=School('oldboy1','北京','私立')print('from repr: ',repr(s1)) #from repr: School(oldboy1,北京)print('from str: ',str(s1)) #from str: (oldboy1,北京)print(s1) #(oldboy1,北京) 默认触发str方法'''str函数或者print函数--->obj.__str__()repr或者交互式解释器--->obj.__repr__()如果__str__没有被定义,那么就会使用__repr__来代替输出注意:这俩方法的返回值必须是字符串,否则抛出异常'''print(format(s1,'nat'))print(format(s1,'tna'))print(format(s1,'tan'))print(format(s1,'asfdasdffd'))#结果为:# oldboy1-北京-私立# 私立:oldboy1:北京# 私立/北京/oldboy1# oldboy1-北京-私立
打印类型的触发
class B: def __str__(self): return 'str : class B' def __repr__(self): return 'repr : class B'b = B()print('%s' % b) #str : class Bprint('%r' % b) #repr : class B
6 __call__
对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo: def __init__(self): pass def __call__(self, *args, **kwargs): print('__call__')obj = Foo() # 执行 __init__obj() # 执行 __call__ #打印出 __call__
7 __del__
析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo: def __del__(self): print('del----->')obj=Foo()print('hehe!')'''结果为:hehe!del----->这种情况是等待这个py的脚本完成执行的时候,触发del命令'''#如果换一种情况呢class Moo: def __del__(self): print('del----->')oby=Moo()del oby #提前触发 del命令print('hehe!')'''结果为:del----->hehe!'''
8 __new__
此方法在实例化的时候触发,它的调用在__init__之前发生,object默认就有此方法
先讲一个模式:单例模式 --》就是一个类只能有一个实例
应用场景
1.当一个类,多次实例化时,每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次
2.当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题
情况一:一般的情况
class A: def __init__(self): self.x = 1 print('in init function') def __new__(cls, *args, **kwargs): print('in new function') return object.__new__(A, *args, **kwargs)a = A()'''in new function #触发__new__,在__init__之前in init function'''print(a.x) # 1b=A() # 结果和a一样
情况二:单例模式
class B: __instance = None def __new__(cls, *args, **kwargs): if cls.__instance is None: obj = object.__new__(cls) cls.__instance = obj return cls.__instance def __init__(self, name, age): self.name = name self.age = age def func(self): print(self.name)a = B('alex', 80) # 实例化,传值print(a.name)b = B('egon', 20) # 实例化,覆盖值print(a)print(b)print(a.name)print(b.name)'''alex<__main__.B object at 0x0000023A1D0B8BA8><__main__.B object at 0x0000023A1D0B8BA8>egonegon它们内存地址一样,这是个单例模式的例子b实例化的时候,传的值覆盖了a'''
9 __eq__
使用==的时候触发
10 item系列
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print('getitem----->') return self.__dict__[item] def __setitem__(self, key, value): print('setitem----->') 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('sb')print(f1['name']) #触发 getitem方法f1['age']=18 #触发setitem方法f1['age1']=19 #触发setitem方法del f1.age1 #触发 delattr方法del f1['age'] #触发 delitem方法f1['name']='alex' #触发setitem方法# print(f1.__dict__)'''执行结果为:getitem----->sbsetitem----->setitem----->del obj.key时,我执行del obj[key]时,我执行setitem----->{'name': 'alex'}'''
11 最后来看一个实例,关于hash和eq
class Person: def __init__(self,name,age,sex): self.name = name self.age = age self.sex = sex def __hash__(self): return hash(self.name+self.sex) def __eq__(self, other): if self.name == other.name and self.sex == other.sex:return Truep_lst = []for i in range(10): p_lst.append(Person('egon',i,'male'))print(p_lst)yy=set(p_lst)print(yy)'''结果为:[<__main__.Person object at 0x00000152982D8DD8>, <__main__.Person object at 0x00000152982D8E10>, <__main__.Person object at 0x00000152982D8EB8>, <__main__.Person object at 0x00000152982E1EB8>, <__main__.Person object at 0x00000152982E1E80>,\ <__main__.Person object at 0x00000152984770F0>, <__main__.Person object at 0x00000152984774E0>, <__main__.Person object at 0x0000015298477518>,<__main__.Person object at 0x0000015298477550>, <__main__.Person object at 0x0000015298477588>]{<__main__.Person object at 0x00000152982D8DD8>}'''通过debug模式调试看到过程:针对set(p_lst)1 集合是个去重的过程,判断一个元素是否是重复的看的是hash值2 调用hash值就触发了__hash__,找到元素的hash值 首先<__main__.Person object at 0x00000152982D8DD8>开始放入集合中,接着<__main__.Person object at 0x00000152982D8E10>也触发__hash__放入集合中3 两个元素的比较接着触发了 __eq__ ,而eq设置的判断标准,它们几乎一样,返回true ,也就是这两个元素的hash判断标准而言,它们是相等的,这样的话,元素<__main__.Person object at 0x00000152982D8E10>被去除了4 以此类推,接着下一个元素5 最后只会剩下唯一的第一个元素