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传入参数


浙公网安备 33010602011771号