python 修饰类对象_Python学习日记(十二)—— 面向对象进阶(类成员:字段,方法,属性、成员修饰符、类的特殊成员)...

面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 的使用

类 是一个模板,模板中包装了多个“函数”供使用(可以讲多函数中公用的变量封装到对象中)

对象,根据模板创建的实例(即:对象),实例用于调用被包装在类中的函数

面向对象三大特性:封装、继承和多态

本篇将详细介绍Python 类的成员、成员修饰符、类的特殊成员。

类的成员

类的成员可以分为三大类:字段、方法和属性

cdc6c3a65f03d8d3b5fd1cc5f1334d8f.png

注:

所有成员中,只有普通字段的内容保存在对象中,即:根据此类创建了多少对象,在内存中就有多少个普通字段。

而其他的成员,则都是保存在类中,即:无论对象的多少,在内存中值创建一份。

一、字段

字段包括:普通字段和静态字段,他们在定义和使用中有所区别,而最本质的区别是内存中保存的位置不同,

普通字段属于对象

静态字段属于类

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classProvince:#静态字段

#静态字段执行的时候,类还没有创建呢,所以不可能放在对象里面,所以保存在类中。

country = '中国'

def __init__(self, name):#普通字段

self.name =name#直接访问普通字段

obj = Province('河北省')printobj.name#直接访问静态字段

Province.country

字段的定义和使用

由上述代码可以看出【普通字段需要通过对象来访问】【静态字段通过类访问】,在使用上可以看出普通字段和静态字段的归属是不同的。其在内容的存储方式类似如下图:

9e44c4e2318b2a969658408e78448d02.png

由上图可知:

静态字段在内存中只保存一份

普通字段在每个对象中都要保存一份

静态字段的应用场景: 通过类创建对象时,如果每个对象都具有相同的字段,那么就使用静态字段

不使用静态字段的情况

class Provice:

# country = "中国"

def __init__(self, name, country):

self.name = name

self.country = "中国"

# 每一个都要写一遍 "中国" ,相同的东西存在了多份,没有必要

# 并且会在内存中保存多次,浪费

hebei = Provice("河北", "中国")

print(hebei.name , hebei.country)

henan = Provice("河南", "中国")

print(henan.name , henan.country)

输出结果:

河北 中国

河南 中国

使用静态字段的情况

class Provice:

#写一个静态字段,让对象自己去调用它,并且只保存了一份,节约空间

country = "中国"

def __init__(self, name):

self.name = name

hebei = Provice("河北")

henan = Provice("河南")

print(henan.name)

print(Provice.country)  #调用静态字段

输出结果:

河南

中国

补充:访问成员的规范

自己去访问自己的成员(类Provice中定义的静态字段就由它自己去调用),除了类中的方法(类中的方法要先创建对象,再用对象去掉用)

# 在python中是可以“胡乱”调用的

class Provice:

country = "中国"

def __init__(self, name):

self.name = name

def show(self):

print('show')

obj = Provice('ciri')

r = obj.country

print(r)

输出结果:中国

# 可以看出,用对象也是可以调用类中的静态字段的

Provice.show(obj) #因为没有创建对象,不会自动传self参数,所以手动写上对象obj

输出结果:show

#可以看出同样,用类也可以调用类中的方法

在python中是支持这四种操作的(类调用方法、类调用字段、对象调用字段、对象调用方法),但是java和c#是严格按照规范来写的,所以为了让自己的代码更条理,还是按照规范来写

二、方法

方法包括:普通方法、静态方法和类方法,三种方法在内存中都属于类,区别在于调用方式不同

普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;

类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;

静态方法:由类调用;无默认参数;

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:def __init__(self, name):

self.name=namedeford_func(self):"""定义普通方法,至少有一个self参数"""

#print self.name

print '普通方法'@classmethoddefclass_func(cls):"""定义类方法,至少有一个cls参数"""

print '类方法'@staticmethoddefstatic_func():"""定义静态方法 ,无默认参数"""

print '静态方法'

#调用普通方法

f =Foo()

f.ord_func()#调用类方法

Foo.class_func()#调用静态方法

Foo.static_func()

方法的定义和使用

b1f28f8b1a9a32378561777c54b2b798.png

相同点:对于所有的方法而言,均属于类(非对象)中,所以,在内存中也只保存一份。

不同点:方法调用者不同、调用方法时自动传入的参数不同。

静态方法的应用场景

class Provice:

def __init__(self,name):

self.name = name

def show(self):

print('show')

#静态方法

@staticmethod #python的内置函数,用来装饰类中的方法。只要一装饰,就变成了静态方法

def xo(): #没有self参数

print('xo')

# 执行静态方法,通过类调用

Provice.xo()

#通过对象也可以,但是要遵守规范,不这么写

obj = Provice('ciri') #加上括号,执行构造方法(init),所以要传个参数

obj.xo()

静态方法的应用:

普通方法想要被执行,要先创建一个对象,然后才能执行;但是静态方法不需要创建对象,直接用类执行,就相当于一个函数放到了类里面。在Java和c#里,可以把静态方法当函数使用,也算是有个函数式编程的办法了。

def denglu(name):

print(name + '登陆成功')

class web:

@staticmethod

def login(name):

print(name + "登陆成功")

#执行函数

denglu('ciri')

#执行静态方法

web.login('ellie')

输出结果:

ciri登陆成功

ellie登陆成功

类方法

作用:就是能获取执行的方法的类名

class Provice:

def __init__(self,name):

self.name = name

def show(self):

print('show')

# 类方法:至少要有一个cls参数,cls全称class

@classmethod

def xxoo(cls): #cls 会把当前类的类名传到cls里面

print('xxoo',cls)

# 类方法也是通过类进行访问

Provice.xxoo()

输出结果:

xxoo

三、属性

Python中的属性其实是普通方法的变种。可以说是完全没什么用处,但有些人写代码的时候用它装逼,还是了解一下,防止看源码的时候看不懂

1、属性的基本使用

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

################ 定义 ###############

classFoo:deffunc(self):pass

#定义属性

@propertydefprop(self):pass

################ 调用 ###############

foo_obj =Foo()

foo_obj.func()

foo_obj.prop#调用属性

属性的定义和使用

f3908f3c80a6389bb78f40719365acbc.png

由属性的定义和调用要注意一下几点:

定义时,在普通方法的基础上添加@property 装饰器;

定义时,属性仅有一个self参数

调用时,无需括号

方法:foo_obj.func()

属性:foo_obj.prop

注意:属性存在意义是:访问属性时可以制造出和访问字段完全相同的假象

属性由方法变种而来,如果Python中没有属性,方法完全可以代替其功能。

Python的属性的功能是:属性内部进行一系列的逻辑计算,最终将计算结果返回,用方法也能办到,但是属性能去括号。haha!

2、属性的操作

对于字段来说,可以获取也能修改

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classProvice:def __init__(self,name):

self.name=name

obj= Provice('alex')print(obj.name)

obj.name= "123" #对于字段来说,能获取也能修改

print(obj.name)

字段的取值和修改

因为特性的本质是讲方法伪造成字段,所以也能进行获取和修改

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:def __init__(self):

self.name= 'a'@propertydefper(self):print('123')return 1obj=Foo()

r=obj.perprint(r)

输出结果:123

1

@property

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:def __init__(self):

self.name= 'a'

#用于执行obj.per

@propertydefper(self):print('123456')#用于接收赋值给per的值

@per.setterdefper(self,val):print(val)

@per.deleterdefper(self):print(666)

obj=Foo()

obj.per= 123

delobj.per

输出结果:123456

123

666

@XXX.setter & @XXX.deleter

其实 @property 和 obj.per 是一种对应关系,同理 @per.setter 和obj.per = 123 以及 @per.deleter 和 del obj.per 也是一种对应关系看到了这样的代码,类中寻找对应的格式,并不执行实际操作。

像 del  obj.per并不会执行删除操作,想要执行删除操作,就在对应的@property、@XXX.setter、@XXX.deleter 方法中写上指定的功能。并且也可以在 @property 方法中,写删除的操作,这个没有规定。

实例:根据用户输入的页码去显示数据

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

li =[]for i in range(10000):

li.append(i)whileTrue:#数据不会全部显示出来,一般的网站都会分页显示

p = input("请输入要查看的页码:") #每页10条

p =int(p)#1 0 - 10

#2页 10-20

#3页 20-30

start = (p-1)*10 #输入1 0-10,输入2 10-20,输入30 290-300

end = p * 10

print(li[start:end])

函数式编程

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classPergination:def __init__(self,current_page):try:

p=int(current_page)exceptException as e:

p= 1self.page=int(current_page)defstart(self):

val= (self.page - 1) * 10

returnvaldefend(self):

val= self.page * 10

returnval

li=[]for i in range(10000):

li.append(i)whileTrue:

p= input("请输入要查看的页码:")

obj=Pergination(p)print(li[obj.start(),obj.end()])

面向对象(正常的代码)

因为最后的输出  print(li[obj.start(),obj.end()])  太乱了,可以用 属性 让代码看上去不那么乱。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classPergination:def __init__(self,current_page):try:

p=int(current_page)exceptException as e:

p= 1self.page=int(current_page)

@propertydefstart(self):

val= (self.page - 1) * 10

returnval

@propertydefend(self):

val= self.page * 10

returnval

li=[]for i in range(10000):

li.append(i)whileTrue:

p= input("请输入要查看的页码:")

obj=Pergination(p)#print(li[obj.start(),obj.end()]) #看上去很凌乱,所以用属性

print(li[obj.start,obj.end]) #骗自己,看上去就不凌乱了

面向对象(属性)

这个实例非常详细的解释了属性的用处,去括号,是的就这么没用。并且什么时候都只有去括号这一个用处!!!!

3、属性的另一种写法

上面的例子是用@property的写法,下面介绍另一种写法

class Foo:

def f1(self):

return 123

per = property(fget=f1) #这里的f1就是上面的f1方法

obj = Foo()

r = obj.per

print(r)

class Foo:

# 上面两句代码就相当于下面的代码

@property

def per(self):

return 123

同理它也有@XXX.setter 、@XXX.deleter相对应的功能

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo:deff1(self):print('ciri')deff2(self,v):print(v)deff3(self):print('del')

per= property(fget=f1,fset=f2,fdel=f3)

obj=Foo()

obj.per

obj.per= 123

delobj.per#同理也是一一对应的关系

fget、fset、fdel

函数的参数最多有三个,还可以加一个注释参数,

per = property(fget=f1,fset=f2,fdel=f3,doc="我是注释,啦啦啦!!!!")

注意:Python WEB框架 Django 的视图中 request.POST 就是使用的静态字段的方式创建的属性

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classWSGIRequest(http.HttpRequest):def __init__(self, environ):

script_name=get_script_name(environ)

path_info=get_path_info(environ)if notpath_info:#Sometimes PATH_INFO exists, but is empty (e.g. accessing

#the SCRIPT_NAME URL without a trailing slash). We really need to

#operate as if they'd requested '/'. Not amazingly nice to force

#the path like this, but should be harmless.

path_info = '/'self.environ=environ

self.path_info=path_info

self.path= '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))

self.META=environ

self.META['PATH_INFO'] =path_info

self.META['SCRIPT_NAME'] =script_name

self.method= environ['REQUEST_METHOD'].upper()

_, content_params= cgi.parse_header(environ.get('CONTENT_TYPE', ''))if 'charset' incontent_params:try:

codecs.lookup(content_params['charset'])exceptLookupError:pass

else:

self.encoding= content_params['charset']

self._post_parse_error=Falsetry:

content_length= int(environ.get('CONTENT_LENGTH'))except(ValueError, TypeError):

content_length=0

self._stream= LimitedStream(self.environ['wsgi.input'], content_length)

self._read_started=False

self.resolver_match=Nonedef_get_scheme(self):return self.environ.get('wsgi.url_scheme')def_get_request(self):

warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or'

'`request.POST` instead.', RemovedInDjango19Warning, 2)if not hasattr(self, '_request'):

self._request=datastructures.MergeDict(self.POST, self.GET)returnself._request

@cached_propertydefGET(self):#The WSGI spec says 'QUERY_STRING' may be absent.

raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')return http.QueryDict(raw_query_string, encoding=self._encoding)################ 看这里看这里 ###############

def_get_post(self):if not hasattr(self, '_post'):

self._load_post_and_files()returnself._post################ 看这里看这里 ###############

def_set_post(self, post):

self._post=post

@cached_propertydefCOOKIES(self):

raw_cookie= get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')returnhttp.parse_cookie(raw_cookie)def_get_files(self):if not hasattr(self, '_files'):

self._load_post_and_files()returnself._files################ 看这里看这里 ###############

POST =property(_get_post, _set_post)

FILES=property(_get_files)

REQUEST= property(_get_request)

Django源码

通过这句来执行不同的函数 -- POST = property(_get_post, _set_post)

obj.POST执行 _get_post

obj.POST = 123执行_set_post

成员小节

规则:自己去访问自己的成员,除了类中的方法

通过类访问的有:静态字段、静态方法、类方法

通过对象访问:普通字段、普通方法、属性

类成员的修饰符

对于每一个类的成员而言,都有两种形式:

共有成员,在任何地方都能访问

私有成员,只有在类的内部才能访问

私有成员和公有成员的定义不同:私有成员命名时,前两个字符是下划线。(特殊成员除外,例如:__init__、__call__、__dict__等)

class Foo:

def __init__():

self.name = '公有字段'

self.__name = '私有字段'

私有成员和公有成员的访问的限制不同:

静态字段

公有静态字段:类可以访问;类内部可以访问;派生类中可以访问

私有静态字段:仅内部可以访问

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classC:

name= "公有静态字段"

deffunc(self):printC.nameclassD(C):defshow(self):printC.name

C.name#类访问

obj=C()

obj.func()#类内部可以访问

obj_son=D()

obj_son.show()#派生类中可以访问

公有静态字段

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classC:__name = "公有静态字段"

deffunc(self):print C.__name

classD(C):defshow(self):print C.__nameC.__name #类访问 ==> 错误

obj=C()

obj.func()#类内部可以访问 ==> 正确

obj_son=D()

obj_son.show()#派生类中可以访问 ==> 错误

私有静态字段

普通字段

公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问

私有普通字段:仅内部可以访问

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classC:def __init__(self):

self.foo= "公有字段"

deffunc(self):print self.foo  #类内部访问

classD(C):defshow(self):printself.foo # 派生类中访问

obj=C()

obj.foo#通过对象访问

obj.func() #类内部访问

obj_son=D();

obj_son.show()#派生类中访问

共有普通字段

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classC:def __init__(self):

self.__foo = "私有字段"

deffunc(self):print self.foo  #类内部访问

classD(C):defshow(self):printself.foo # 派生类中访问

obj=C()

obj.__foo #通过对象访问 ==> 错误

obj.func() #类内部访问 ==> 正确

obj_son=D();

obj_son.show()#派生类中访问 ==> 错误

私有普通字段

PS:如果想要强制访问私有字段,可以通过【对象._类名__私有字段名】访问(如obj._C__Foo),不建议这么用,忘记即可

class Foo:

def __init__(self):

self.__name = "ciri"

obj = Foo()

print(obj._Foo__name)

方法、属性的访问和上述方式相似,即:私有成员只能在类内部使用

ps:强制访问私有方法,私有属性

class Foo:

__ox = "ox"

def __init__(self):

self.__name = "ciri"

def __fetch(self):

print('私有普通方法')

@staticmethod

def __sta():

print('私有静态方法')

@property

def __per(self):

print('私有属性')

@__per.setter

def __per(self,val):

print(val)

obj = Foo()

print(obj._Foo__name) #强制访问私有字段

print(obj._Foo__ox) #强制访问静态私有字段

obj._Foo__fetch() #强制访问私有普通方法

obj._Foo__sta() #强制访问私有静态方法

obj._Foo__per #强制访问私有属性

obj._Foo__per = '赋值的对应'

类的特殊成员

上文介绍了Python的类成员以及成员修饰符,从而了解到类中有字段、方法和属性三大类成员,并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用。

无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下:

1.__doc__

表示类的描述信息

class Foo:

"""我是类的描述信息"""

def func(self):

pass

print Foo.__doc__

输出:

我是类的描述信息

2.__module__ 和 __class__

__module__   表示当前操作的对象在那个模块

__class__       表示当前操作的对象的类是什么

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#!/usr/bin/env python#-*- coding:utf-8 -*-

classC:def __init__(self):

self.name= 'wupeiqi'

我是被下面代码调用的文件,路径lib/aa.py

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

from lib.aa importC

obj=C()print obj.__module__ #输出 lib.aa,即:输出模块

print obj.__class__ #输出 lib.aa.C,即:输出类

index.py

3. __init__

构造方法.通过类创建对象时,自动触发执行。

class Foo:

def __init__(self, name):

self.name = name

self.age = 18

obj = Foo('ciri') # 加括号会自动执行类中的 __init__ 方法

4. __del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。但是对象什么时候被销毁是不知道的,所以用处不大。

class Foo:

def __del__(self):

pass

5. __call__

对象后面加括号,触发执行。

注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:

def __init__(self):

print('init')

def __call__(self, *args, **kwargs):

print('call')

r = Foo() #执行init方法

r() #对象后面加括号,只执行call

Foo()() #先执行init后执行call

输出:

init

call    #对象加括号执行一个

init

call

6. __dict__

返回类或对象中的所有成员

上文中我们知道:类的普通字段属于对象;类中的静态字段和方法等属于类,即:

class Foo:

"""

我是类的解释

"""

def __init__(self,name,age):

self.name = name

self.age = age

self.n = 123

#以字典的形式返回,对象中封装的内容

obj = Foo('ciri',18)

print(obj.__dict__)

输出结果:

{'name': 'ciri', 'age': 18, 'n': 123}

#返回类里面的可见成员

ret = Foo.__dict__

print(ret)

输出结果:

{

'__module__': '__main__',

'__doc__': '\n 我是类的解释\n ',

'__init__': ,

'__dict__': ,

'__weakref__':

}

7. __int__、__str__

如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值。

class Foo:

def __init__(self):

pass

def __int__(self):

return 1111

def __str__(self):

return "ciri"

obj = Foo()

print(obj,type(obj))

# int后面加对象,会自动执行对象的__int__方法,并将返回值自动赋值给int的对象

r = int(obj)

print(r)

# str后面加对象,会自动执行对象的__str__方法,并将返回值自动赋值给str的对象

i = str(obj)

print(i)

__int__方法几乎用不到,__str__方法常用,比如:

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('ciri',18)

print(obj)

输出结果:

ciri-18

print()怎么就执行了str?因为print内部执行了两个步骤

步骤一:内部执行print(str(obj))

步骤二:str(obj)在内部又会调用obj中的__str__并获取返回值

8、__getitem__、__setitem__、__delitem__

用于索引操作,如字典。以上分别表示获取、设置、删除数据

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('alex',18)

# 自动执行li对象类中的__getitem__方法,8当做参数传递给item

r = li[8] #索引格式和getitem是对应关系

print(r)

li[100] = "asdf" #和setitem是对应关系

del li[999] #和delitem是对应关系

上面的代码,就只有getitem方法有返回值,因为获取值要有东西接受它。但是设置和删除没必要。对应语法和方法名仅仅是个对应关系。

Python3中用__getitem__进行切片操作,当然对切片的操作比较少,用的多的还是索引。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo():def __init__(self,name,age):

self.name=name

self.age=agedef __getitem__(self, item):print(item,type(item))

li= Foo('ciri',16)

li[123]

li[1:4:3] #把这三个值封装到对象里面了,再把对象传进去

输出结果:123 slice(1, 4, 3)

通过封装切片,对切片进行操作

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo():def __init__(self,name,age):

self.name=name

self.age=agedef __getitem__(self, item):#通过判断来区别是传入的参数是索引还是切片

#如果item是基本类型:str,int,则为索引

#如果是slice对象,这么切边类型

if type(item) ==slice:print("内部做切片处理")else:print("内部做索引处理")

li= Foo('ciri',16)

li[123]

li[1:4:3]

输出结果:

内部做索引处理

内部做切片处理

如何对切片和索引进行不同的操作?

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classFoo():def __init__(self,name,age):

self.name=name

self.age=agedef __getitem__(self, item):if type(item) ==slice:print("内部做切片处理")print(item.start) #开头

print(item.stop) #结尾

print(item.step) #步长

else:print("内部做索引处理")

li= Foo('ciri',16)

li[123]

li[1:4:3]

如何拿到slice对象里面的值

slice对象中用start,stop,step来取切片中对应的开头,结尾和步长

9、__getslice__、__setslice__、__delslice__

python2中特有的,python3中还是用__getitem__操作列表

该三个方法用于分片操作,如:列表

#!/usr/bin/env python

# -*- coding:utf-8 -*-

class Foo(object):

def __getslice__(self, i, j):

print '__getslice__',i,j

def __setslice__(self, i, j, sequence):

print '__setslice__',i,j

def __delslice__(self, i, j):

print '__delslice__',i,j

obj = Foo()

obj[-1:1] # 自动触发执行 __getslice__

obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__

del obj[0:2] # 自动触发执行 __delslice__

10. __iter__

用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 __iter__

回顾之前的内容:

什么是迭代器?

满足两个条件:1.有iter方法  2.有next方法

for循环后面加的是什么?

可迭代对象

什么是可迭代对象?

现象上——能进行 for循环的,都是可迭代对象

本质上——内部有iter方法的,就是可迭代对象

创建和执行迭代器

li = [1,2,3,4]

d = iter(li) #iter方法就做了一件事情,返回了一个迭代器对象

print(next(d))

print(next(d))

须知:

i = [11,22,33]

i = iter([11,22,33])

for item in i :

print(item)

对于for循环

遇到迭代器,可以直接循环

遇到可迭代对象,执行对象的类中__iter__方法获取迭代器,然后进行循环

定义:如果类中有__iter__方法,创建的对象就是可迭代对象

对象.__iter__()方法的返回值,是一个迭代器

__iter__方法的使用

class Foo:

def __init__(self,name,age):

self.name = name

self.age = age

def __iter__(self):

return iter([11,22,33])  #要返回一个迭代器

li = Foo('alex', 18)

for i in li:

print(i)

11. __new__ 和 __metaclass__

class Foo(object):

def __init__(self):

pass

obj = Foo() # obj是通过Foo类实例化的对象

上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。所以:

obj是对象,是Foo类的对象

Foo类也是一个对象,是type的对象

如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。

print type(obj)

# 输出: 表示,obj 对象由Foo类创建

print type(Foo)

# 输出: 表示,Foo类对象由 type 类创建

所以,obj对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。

那么,创建类就可以有两种方式:

a). 普通方式

class Foo(object):

def func(self):

print 'ciri'

b).特殊方式(type类的构造函数)

def function(self):

print 'ciri'

Foo = type('Foo',(object,), {'func': function})

#type第一个参数:类名

#type第二个参数:当前类的基类

#type第三个参数:类的成员

所以,类是由 type类实例化产生

那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?

答:类中有一个属性 __metaclass__,其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。同时也因为type是由C语言底层实现,在python中看不到源码,所以也需要间接去证明这一点。

创建一个MyType类,去继承type类,让Foo类创建的时候,指定一下,让MyType去创建。这样,就可以通过验证MyType去验证type的作用。

问题:为什么MyType要继承type类?

因为要继承type类创建类的功能,是类能够顺利创建出来。就是因为创建类需要type的init方法,继承了这个方法后创建Foo类的时候就可以调用这个方法,假如说不继承type类,就不知道怎么去创建类了。

class MyType(type):

def __init__(self,*args,**kwargs):  #只要init方法被调用,就会输出123

print(123)

class Foo(object,metaclass=MyType):  #表示该类由MyType实例化创建

def func(self):

print("ciri")

输出结果:

123

可以看创建Foo类,执行了MyType的init方法

由此验证:创建了一个类,要调用type的init方法

代码继续往下执行

class MyType(type):

def __init__(self,*args,**kwargs):

print(123)

def __call__(self, *args, **kwargs):

print(456)

class Foo(object, metaclass=MyType):

def __init__(self):

pass

def func(self):

print("ciri")

obj = Foo()

输出结果:

123

456

# 可以看出,Foo加括号执行了MyType的call方法

因为对象后面加括号会执行call方法,以及Foo类是MyType的对象。

所以Foo类后面加括号即Foo(),会执行MyType类的__call__方法

class Foo:

def __init__(self):

pass

def __call__(self, *args, **kwargs):

pass

obj = Foo()#执行init方法

obj()#执行call方法

执行到class Foo(object,metaclass)是调用init方法,执行到obj = Foo()调用call方法,此时class Foo(object,metaclass)里面的内容还没有被执行。要想执行里面的内容,要在call方法中调用里面的东西(__new__方法,在new方法中,真正的创建了obj)

#将程序写成这样是为了方便理解,并不能够运行,因为new并没有创建方法

class MyType(type):

def __init__(self,*args,**kwargs):

print(123)

def __call__(self, *args, **kwargs):

#self = Foo self就是Foo

r = self.__new__() #因为self就是Foo,所以self.__new__()就是Foo.__new__(),所以这句就是去Foo里执行new方法

print(r)

class Foo(object, metaclass=MyType):

def __init__(self):

pass

def __new__(cls, *args, **kwargs):

return "对象" #这里返回的就是obj

def func(self):

print("ciri")

obj = Foo()

补充:MyType里的self参数是Foo,并不是obj,因为执行的时候obj还没有创建出来。

图片中的__metaclass__ = MyType是2.7中的写法

27fc322d3f3f1c539875c36231328471.png

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

classMyType(type):def __init__(self, what, bases=None, dict=None):

super(MyType, self).__init__(what, bases, dict)def __call__(self, *args, **kwargs):

obj= self.__new__(self, *args, **kwargs)

self.__init__(obj)classFoo(object):__metaclass__ =MyTypedef __init__(self, name):

self.name=namedef __new__(cls, *args, **kwargs):return object.__new__(cls, *args, **kwargs)#第一阶段:解释器从上到下执行代码创建Foo类#第二阶段:通过Foo类创建obj对象

obj = Foo()

与图对应的代码

最后关于这记住两点就可以了

init执行之前还执行好几个方法

如果想自己定义一个type,搞一个mytype就可以了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值