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

 

posted @ 2018-05-04 09:32  Reid21  阅读(117)  评论(0编辑  收藏  举报