Python攻克之路-类的特殊成员
一、成员修饰符
场景描述:有公有和私有成员修饰符,如果要做链接数据库的操作,要把用户名和密码封装到数据库中,所以不希望其他人知道,因此设置成私有的,如果真想其他人看,可以写一个公共的方法
1、公有的
[root@node2 class]# cat mem.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): self.name = name self.age = age obj = Foo('reid', 30) print(obj.name) print(obj.age) [root@node2 class]# python3 mem.py reid 30
2、私有的:不能够直接访问,只能间接访问,在内部访问,它的主要目的还是不允许外部访问
普通字段
[root@node2 class]# cat mem.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): self.name = name self.__age = age def show(self): ##通过内部的一个方法来操作 return self.__age obj = Foo('reid', 30) print(obj.name) ret = obj.show() print(ret) [root@node2 class]# python3 mem.py reid 30
静态字段
[root@node2 class]# cat static.py #!/usr/local/python3/bin/python3 class Foo: v = '123' def __init__(self): pass print(Foo.v) [root@node2 class]# python3 static.py 123
创建对象来访问私有的
[root@node2 class]# cat static.py #!/usr/local/python3/bin/python3 class Foo: __v = '123' def __init__(self): pass def show(self): ## return Foo.__v ## ret = Foo().show() ## print(ret) [root@node2 class]# python3 static.py 123
通过静态方法来访问
[root@node2 class]# cat static.py #!/usr/local/python3/bin/python3 class Foo: __v = '123' def __init__(self): pass def show(self): return Foo.__v @staticmethod def stat(): return Foo.__v ret = Foo.stat() print(ret) [root@node2 class]# !py python3 static.py 123
3、方法的公有和私有
正常的方法访问
[root@node2 class]# cat fuc.py #!/usr/local/python3/bin/python3 class Foo: def f1(self): return 123 obj = Foo() ret = obj.f1() print(ret) [root@node2 class]# python3 fuc.py 123
通过内部方法访问私有的
[root@node2 class]# cat fuc.py #!/usr/local/python3/bin/python3 class Foo: def __f1(self): return 123 def f2(self): # r = self.__f1() # return r # obj = Foo() ret = obj.f2() print(ret) [root@node2 class]# python3 fuc.py 123
4、私有字段:无法通过继承来访问父类的私有字段,只能在一个类的内部
[root@node2 class]# cat pri.py #!/usr/local/python3/bin/python3 class F: def __init__(self): self.ge = 123 self.__gene = 345 ##无法访问 class S(F): def __init__(self,name): self.name = name self.__age = 29 ##可以访问,在内部 super(S,self).__init__() def show(self): print(self.name) print(self.age) print(self.__age) print(self.ge) print(self.__gene) s = S('reid') s.show()
二、类的特殊成员
__init__ 类()自动执行
__call__ 对象() 类()()自动执行
__int__ int(对象)
__str__ str()
__add__ 少用
__del__ 被销毁时触发(并不多用)
__dict__ *****
__getitem__ 切片(slice类型)或者索引
__setitem__
__delitem__
__iter__
1. int,str
场景:对象后面加括号,自动触发某个方法
[root@node2 class2]# cat special-member.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self): print('init') def __call__(self, *args, **kwargs): print('call') obj = Foo() #类后加括号,自动执行__init__ obj() #对象后面加括号,自动执行__call__ ,相当于Foo()() [root@node2 class2]# python3 special-member.py init call
字符串的情况
In [1]: s = '123' #相当于s = str('123'),python内部做了字符串的转换 In [2]: i = int(s) #转换成整数 In [3]: print(i,type(i)) 123 <class 'int'>
类转换的情况
Foo类型
[root@node2 class]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self): pass obj = Foo() print(obj,type(obj)) [root@node2 class]# python3 cls.py <__main__.Foo object at 0x7f684bc0d320> <class '__main__.Foo'>
对象转换成整数类型
[root@node2 class]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self): pass obj = Foo() print(obj, type(obj)) r = int(obj) print(r) [root@node2 class]# python3 cls.py #转换报错,因为通过int转换时,它在内部也会执行一下,如果把一个对象,如果把对象放入Int就是要转换成int, 在它的内部就会自动执行一个方法,这个特殊的方法返回值是什么,int的值就是多少 <__main__.Foo object at 0x7f45a84ce320> <class '__main__.Foo'> Traceback (most recent call last): File "cls.py", line 8, in <module> r = int(obj) TypeError: int() argument must be a string, a bytes-like object or a number, not 'Foo' [root@node2 class]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self): pass def __int__(self): ###### return 222 ###### obj = Foo() print(obj, type(obj)) r = int(obj) #int后面加上对象,它就会自动执行对象的__int__方法,并将返回值赋值给int对象 print(r) [root@node2 class]# python3 cls.py <__main__.Foo object at 0x7f36919c8320> <class '__main__.Foo'> 222
对象转换成字符串(比较常用)
[root@node2 class]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self): pass def __int__(self): return 222 def __str__(self): ### return 'reid' ### obj = Foo() print(obj, type(obj)) r = str(obj) ### print(r) [root@node2 class]# python3 cls.py reid <class '__main__.Foo'> reid
对象转换成字符串:主要使用于打印结果
正常情况下:输出内存地址,无法判断封装了什么 [root@node2 class]# cat ex.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,n,a): self.name = n self.age = a obj = Foo('reid',29) print(obj) [root@node2 class]# python3 ex.py <__main__.Foo object at 0x7f646daca390> [root@node2 class]# cat ex.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,n,a): self.name = n self.age = a def __str__(self): return self.name obj = Foo('reid',29) print(obj) #print内部会调用__str__方法,调用对象,打印时是显示name #print实际两步操作:print(str(obj))和str(obj),obj中的__str__,并获取其返回值 [root@node2 class]# cat ex.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,n,a): self.name = n self.age = a def __str__(self): return '%s : %s' %(self.name,self.age,) #格式化输出就可以看见 obj = Foo('reid',29) print(obj) [root@node2 class]# python3 ex.py reid : 29
2. __add__:两个对象相加时,会自动执行第一个对象的__add__方法,并且第二个对象传入other
[root@node2 class2]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): self.name = name self.age = age obj1 = Foo('reid',30) obj2 = Foo('lin',33) r = obj1 + obj2 print(r,type(r)) [root@node2 class2]# python3 cls.py Traceback (most recent call last): File "cls.py", line 9, in <module> r = obj1 + obj2 TypeError: unsupported operand type(s) for +: 'Foo' and 'Foo' ##不能相加 [root@node2 class2]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): self.name = name self.age = age def __add__(self, other): #####自动调用它 # self = obj1 ('reid',30) # other = obj2 ('lin',33) return "ok" ##### obj1 = Foo('reid',30) obj2 = Foo('lin',33) r = obj1 + obj2 print(r,type(r)) [root@node2 class2]# python3 cls.py ok <class 'str'> [root@node2 class2]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): self.name = name self.age = age def __add__(self, other): return self.age + other.age ##返回两个数相加 obj1 = Foo('reid',30) obj2 = Foo('lin',33) r = obj1 + obj2 print(r,type(r)) [root@node2 class2]# python3 cls.py 63 <class 'int'> 返回一个Foo的对象obj3,obj1是('reid',30),obj2是('lin',33),需求是返回一个对象reid和33, 也就是返回什么就是什么,返回一个对象,Foo('')类后面加上括号就是一个对象,并且执行name,age, 就相当于内部封装值,如封装一个tt,99对象就返回 [root@node2 class2]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): self.name = name self.age = age def __add__(self, other): return Foo('tt',99) ##### obj1 = Foo('reid',30) obj2 = Foo('lin',33) r = obj1 + obj2 print(r,type(r)) [root@node2 class2]# python3 cls.py <__main__.Foo object at 0x7fe4b68dd518> <class '__main__.Foo'> ###Foo对象, 对象类型 [root@node2 class2]# cat cls.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): self.name = name self.age = age def __add__(self, other): return Foo(obj1.name,other.age) ##混合 obj1 = Foo('reid',30) obj2 = Foo('lin',33) r = obj1 + obj2 print(r,type(r)) [root@node2 class2]# python3 cls.py <__main__.Foo object at 0x7fadb8f854e0> <class '__main__.Foo'>
3. 稀构方法:__del__
分析:对象创建时,自动执行init方法,创建完,就在内存里开辟空间,当没有被使用时,会被销毁,java等内存的处理都不再需要程序员来做,java有它的虚拟机,自动实现垃圾回收机制,检测代码里是否有一个对象是否被占用,如果没有就会被回收,python也有自己的回收机制
因此,构造方法是创建对象时触发的,对于稀构方法是对象被销毁时触发的,并不知道什么时候被执行使用场景:写了一个关于文件操作的类,当创建实例时,可以使用类打开一个文件,中间可以一直对文件操作,到最后可能不会使用它,这时可以写一个稀构方法,在稀构方法中写上关闭文件,当对象被销毁时,文件就会自动关闭掉
__dict__ : 将对象中封装的所有内容通过字典的形式返回 *** [root@node2 class2]# cat dic.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self, name, age): #正常情况下,封装的内容是不可见的 self.name = name self.age = age self.n = 123 obj = Foo('reid',10) d = obj.__dict__ #通过__dic__来显示__init__内部封装的内容 print(d) [root@node2 class2]# python3 dic.py {'name': 'reid', 'age': 10, 'n': 123} #以字典形式返回
4. __dict__:显示类的成员
描述:python内部会增加很多类的成员,如注释,描述当前类的功能,类似于静态字段,还有如类在那个模块中,类中有很多其他的成员,这个类是如何创建的,都会创建出来
[root@node2 class2]# cat dic.py #!/usr/local/python3/bin/python3 class Foo: ''' show the person info''' def __init__(self, name, age): self.name = name self.age = age self.n = 123 d = Foo.__dict__ print(d) [root@node2 class2]# python3 dic.py {'__module__': '__main__', '__doc__': ' show the person info', '__init__': <function Foo.__init__ at 0x7f57421230d0>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>} In [1]: li = [11,22,33,44] In [2]: r1 = li[3] In [3]: print(r1) 44
5. getitem, setitem,delitem的使用 *****
__getitem__: 是使用对象后加中括号的形式访问
list一般操作
In [1]: li = [11,22,33,44] 列表,实际写法是li = list(),这也是创建一个对象,里面有很多方法 In [2]: r1 = li[3] 根据索引到列表中取值,li[3]这个索引,实际内部是执行一个特殊方法,是一个语法 In [3]: print(r1) 44 In [4]: li[3] = 666 根据索引给列表某个索引赋值,同样会执行一个特殊的方法 In [5]: del li[2] 根据索引把某个值删除,同样执行一个特殊的方法
类的情况:要实现具体的操作,只能在方法中设置
[root@node2 class2]# cat li.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self,item): ##获值 return item+10 li = Foo('reid',19) r = li[8] #会自动执行li对象的类中__getitem__的方法,8会当作参数传入给item,切片的操作li[1:3:3]也是调用getitem print(r) [root@node2 class2]# python3 li.py 18
赋值
[root@node2 class2]# cat li.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self,item): return item+10 def __setitem__(self,key,value): ### print(key,value) li = Foo('reid',19) r = li[8] print(r) li[100] = 'jerry' ### [root@node2 class2]# python3 li.py 18 100 jerry
删除值
[root@node2 class2]# cat li.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self,item): return item+10 #只有它有返回值,获取时要返回 def __setitem__(self,key,value): print(key,value) #设置时不需要返回 def __delitem__(self,key): #### print(key) li = Foo('reid',19) r = li[8] print(r) li[100] = 'jerry' ####无法接收返回值 del li[999] #### [root@node2 class2]# python3 li.py 18 100 jerry 999
li[1:4:2]切片分析
正常函数传参
[root@node2 class2]# cat test.py #!/usr/local/python3/bin/python3 def func(arg): print(arg) a = 123 func(a) [root@node2 class2]# python3 test.py 123
正常传入对象
[root@node2 class2]# cat test.py #!/usr/local/python3/bin/python3 class Bar: def func(arg): print(arg) obj = Bar() a = 123 a = Foo() obj.func(a) #当对象点时,可以传入数字,也可以传入对象, arg是可以传入任何东西 [root@node2 class2]# cat li.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self,item): ### print(item,type(item)) ### def __setitem__(self,key,value): print(key,value) def __delitem__(self,key): print(key) li = Foo('reid',19) li[123] li[1:4:2] [root@node2 class2]# python3 li.py 123 <class 'int'> 整型123 slice(1, 4, 2) <class 'slice'> #是个类,对象可以封装多个值,相当于对象帮忙做了个封装,它把1:4:2这三个值封装到一个对象中, 然后把这个对象传递到item中
li[1:4:2]通过冒号进行split分开,它们就变成三个值1,4,2
class Slice: def __init__(self,a,b,c): self.start = a self.end = b self.step =c obj = Slice(1,4,2) #把obj做统一的参数传入item
getitem中实现判断不同的情况
[root@node2 class2]# cat li.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self,item): #如果item是基本类型,int,str,索引获取,slice对象的放,切片 if type(item) == slice: print('slice') else: print('index') def __setitem__(self,key,value): print(key,value) def __delitem__(self,key): print(key) li = Foo('reid',19) li[123] li[1:4:2] [root@node2 class2]# python3 li.py index slice
切片写法
[root@node2 class2]# cat li.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age def __getitem__(self,item): if type(item) == slice: print(item.start) #把起始,结束,步长取得,再做处理 print(item.stop) # print(item.step) # print('slice') else: print('index') def __setitem__(self,key,value): ##相应的setitem,delitem,如果是切分,key也会变成slice对象,就判断item中是否有冒号之类就是slice print(key,value) def __delitem__(self,key): print(key) li = Foo('reid',19) li[123] li[1:4:2]
6. __iter__方法
列表情况
In [1]: li=[11,22,33,44] #li=list(11,22,33,44),list是一个可迭代的对象 In [2]: for item in li: #li是可迭代对象 ...: print(item) ...: 11 22 33 44
判断类是否是可迭代对象
[root@node2 class2]# cat ite.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age li = Foo('reid',19) for i in li: print(i) #如果类中有__iter__方法,创建的对象就是可迭代对象,对于__iter__()的返回值是一个迭代器 #for循环中有迭代器,直接是next #如果for循环中是可迭代对象,先得到迭代器,再执行next #1.获取li对象的类Foo中的__iter__方法,并获取其返回值 #2.循环上一步中返回的对象,循环的实际是对象iter的返回值 [root@node2 class2]# python3 ite.py Traceback (most recent call last): File "ite.py", line 8, in <module> for i in li: TypeError: 'Foo' object is not iterable #for循环操作时,会执行对象的点next,如果没有next会不继续执行,如果手动执行next,没有会报错
手动
i = iter([11,22,33,44]) i.next() i.next() i.next() i.next() i.next() #到第五个没有就会报错
for循环迭代时,自动执行next,i直接是迭代器
for item in i: print(item)
i是是可迭代对象时,会先执行对象的Iter方法,取得迭代器,然后for循环迭代
[root@node2 class2]# cat li.py #!/usr/local/python3/bin/python3 class Foo: def __init__(self,name,age): self.name = name self.age = age def __iter__(self): return iter([11,22,33,44]) li = Foo('reid',19) for i in li: #li是可迭代对象Foo('reid',19),因为里面有__iter__方法,for循环进行可迭代对象时,先执行可迭代对象的iter方法, #取得返回值就是一个迭代器,再对迭代器进行一个个的迭代 print(i) [root@node2 class2]# python3 li.py 11 22 33 44
7.metaclass ****
描述:面向对象,class是一个类,根据类可以创建对象,类后面加括号取得一个对象,在Python中,一切事物是对象,这样,通过类创建一个
对象,类是Python的,它也是对象,
(1). Python中一切事物是对象
(2).class Foo:
pass
obj = Foo()
#obj是对象,是Foo类的对象
#Foo类也是一个对象,是type的对象
(3). 类都是type类的对象[type()]
"对象" 都是以类的对象[类()]
创建类的两种方式(也就是声明一个类)
a.普通方式
class(object): def func(self): print('hello reid')
b.特殊方式(type类的构造函数)
def function(self): print('hello reid') Foo = type('Foo',(object,),{'func': function}) #type后有一个括号,相当于创建一个对象(声明一个类,类中有一个成员func) #type第一个参数:类名 #type第二个参数:当前类的基类,继承谁,默认object,'Foo',(object,)是写上一个类继承Object #type第三个参数:类的成员,有一个变量名是func,func=function, # object: 几乎所有语言,一旦写上类后class Foo,默认是没加东西,实际默认在内部就继承object的类(如java,c#),object是一个超级类
metaclass分析过程
(1)、类创建好后,默认就会创建type的构造方法,代码从上到下解释,创建一个类这个数据类型,就直接放入到内存中,实际是执行以下代码时,会创建type对象
class Foo(object): def func(self): print 'hello'
(2)、先type类中的Init方法,再创建对象
[root@node2 class2]# cat tp.py #!/usr/local/python3/bin/python3 class MyType(type): #写一个type的子类,继承type,再指定使用这个type去创建,这样会优先去执行自定义type的init方法 def __init__(self,*args,**kwargs): print(123) #如果123能打印,表示创建类时,就调用了这个init方法 class Foo(object,metaclass=MyType): #实际在内部自动执行type的init方法,但是这个type是不可见的,这里通过metaclass=MyType告诉它MyType的存在,创建类时使用MyType去创建 def func(self): print('hello') [root@node2 class2]# python3 tp.py 123
(3)、实际类加括号,在执行init之前,中间还有好几步操作
分析:Foo是MyType的对象,对象加括号会执行类的__call__方法,当obj = Foo()没写,整个从上到下解释,解释到一旦出现Foo,会执行print('123')的操作,如果从上到下执行到obj = Foo(),这时class Foo中的都不执行,Foo类是MyType的对象,对象后面加括号Foo(),会执行类class MyType的__call__方法
[root@node2 class2]# cat tp.py #!/usr/local/python3/bin/python3 class MyType(type): def __init__(self,*args,**kwargs): print(123) class Foo(object,metaclass=MyType): def __init__(self): pass def func(self): print('hello') obj = Foo() #类加括号默认会执行__init__方法 [root@node2 class2]# cat tp.py #!/usr/local/python3/bin/python3 class MyType(type): def __init__(self,*args,**kwargs): print(123) def __call__(self,*args,**kwargs): print(456) #类执行它,表示Foo是MyType的对象,对象括号会自动执行类的__call__方法 class Foo(object,metaclass=MyType): ##3.x的写法metaclass=MyType def __init__(self): pass def func(self): print('hello') obj = Foo() [root@node2 class2]# python3 tp.py 123 456
(4)、通过类MyType的__call__方法来执行class Foo
(5)、调用流程
分析:代码从上到下执行到,class Foo时,表示要创建类__metaclass__ = MyType(2.7写法),这个类由MyType来创建,然后去执行MyType的__init__的方法,super()的是执行父类中的init的方法,因为在type中可能还会有其他的功能,不应该阻止它的其他功能,第一阶段完成
当执行obj = Foo()时,就是进入第二阶段了,首先会执行MyType中的call方法,call中的self是Foo类,然后它再执行Foo.__new__方法(class Foo中的new方法),它return了object.__new__(cls, *args,**kwargs),真正创建对象也是由它来实现的,object中的cls是Foo,整行代码就是创建Foo的对象,再返回到MyType中call方法中,所以call中的obj就是Foo对象
self.__init__(obj):这里的self也是Foo,也就是执行class Foo中的init的方法,把obj传入参数