一面向对象的结构和成员
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.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.__name # 不可在外部访问 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.__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_price obj = Goods() obj.price # 获取商品价格 obj.price = 200 # 修改商品原价 del obj.price # 删除商品原价
二面向对象的内置函数
1 isinstance和issubclass
isinstance() 与 type() 区别:
type() 不会认为子类是一种父类类型,不考虑继承关系。
isinstance() 会认为子类是一种父类类型,考虑继承关系。
#isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class A:pass class B(A):pass abj=B() print(isinstance(abj,B)) #True print(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) #xincheng print(hasattr(obj,'name')) #True 检查是否含有name的属性 #获取 my_name=getattr(obj,'name') #获取某个对象的属性 print(my_name) #xincheng func=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) #hehe setattr(obj,'show_name',lambda self:self.name+'sb') #此属性相当于设置了一个函数 print(obj.__dict__) #{'name': 'xincheng', 'age': 73, 'sb': 'hehe', 'show_name': <function <lambda> 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 # <function Foo.func at 0x000002903EE65730> # <function Foo.bar at 0x000002903EE657B8> func=getattr(Foo,'bar') print(func) ## <function Foo.bar at 0x000002903EE657B8> 内存地址和类的bar方法一样
III对当前模块
def login(): print(888) msg=input('>>>>').strip() import sys print(sys.modules) this_module=sys.modules[__name__] print(this_module) #<module '__main__' from 'G:/day8/练习.py'> print(hasattr(this_module,'login')) #True if hasattr(this_module,msg): getattr(this_module,msg)() else: print('没有此属性')
IIII对另外一个模块
#模块名称模块测试反射,内容如下 def test(): print('来自于另外一个模块') #本地操作: import 模块测试反射 as obj print(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 B print('%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) # 1 b=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> egon egon 它们内存地址一样,这是个单例模式的例子 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-----> sb setitem-----> 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 True p_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 最后只会剩下唯一的第一个元素