面向对象【day08】:类的特殊成员-类的起源与metaclass

本节内容

  1. 类的特殊成员方法
  2. 类的起源于metaclass

一、类的特殊成员方法

一、概述

  前面我们讲了类的方法,有普通方法,就是我们自己定义的方法,还有静态方法,类方法,属性方法,其实类还有另外一种方法,叫做类的特殊成员方法

1 __doc__

说明:表示类的描述信息


1

2

3

4

5

6

7

8

9

10

11

class Dog(object):

    """此类是形容Dog这个类"""    #类的描述信息

    def __init__(self,name):

        self.name = name

print(Dog.__doc__)   #打印类的描述信息

#输出

此类是形容Dog这个类

2 __module__和__class__

说明:

  1. __module__: 表示当前操作的对象在哪个模块
  2. __class__:表示当前操作的对象的类是什么

aa.py的代码:


1

2

3

4

class C(object):

    def __init__(self):

        self.name = "shuaigaogao"

index.py的代码:


1

2

3

4

5

6

7

8

9

10

from lib.aa import C

obj = C()

print(obj.__module__) #表示当前操作的对象在哪个模块

print(obj.__class__)  #表示当前操作的对象的类是什么

#输出

lib.aa

<class ‘lib.aa.C‘>

3 __init__

说明:构造方法,通过类创建对象时,自动触发执行

4 __del__

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


1

2

注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,

所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的

5 __call__

说明: 对象后面加括号,触发执行


1

2

3

4

5

6

7

8

9

10

11

12

class Foo(object):

    def __init__(self):

        self.name = "shuaigaogao"

    def __call__(self*args, **kwargs):  #重写call方法

        print("running call",args,kwargs)

= Foo()   #执行__init__

f(1,2,3,name=333)  # 执行call方法,也可以写成 Foo()(1,2,3,name=333)

#输出

running call (123) {‘name‘333}

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

6 __dict__

说明: 查看类或对象中的所有成员

①类.__dict__

效果:打印类中所有的属性,不包括实例属性


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

class Province(object):

    country = ‘China‘

    def __init__(self, name, count):

        self.name = name

        self.count = count

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

        print("func")

print(Province.__dict__) #类.__dict__

#输出

{‘__doc__‘None‘__weakref__‘: <attribute ‘__weakref__‘ of ‘Province‘ objects>, ‘__init__‘:

<function Province.__init__ at 0x00000247F3CAD488>, ‘country‘‘China‘‘__dict__‘:

<attribute ‘__dict__‘ of ‘Province‘ objects>, ‘func‘: <function Province.func at

0x00000247F3CAD510>, ‘__module__‘‘__main__‘}  #打印类中所有的属性,不包括实例属性

②实例名.__dict__

效果:打印该实例的所有属性,不包括类属性


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

class Province(object):

    country = ‘China‘

    def __init__(self, name, count):

        self.name = name

        self.count = count

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

        print("func")

= Province("jiangsu",20000)  #实例化

print(p.__dict__) #实例名.__dict__

#输出

{‘count‘20000‘name‘‘jiangsu‘}  #打印该实例的所有属性,不包括类属性

7 __str__

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


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class Province(object):

    country = ‘China‘

    def __init__(self, name):

        self.name = name

    def __str__(self):

        return "<obj:{0}>".format(self.name)

= Province("jiangsu")

print(p)  #打印这个对象

#输出

<obj:jiangsu>  #给对象重新起了一个名字

注:这个以后会在django框架里面会用到,这边就不多说了

8 __getitem__、__setitem__、__delitem__

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


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

class Foo(object):

    def __getitem__(self, key):

        print(‘__getitem__:‘,key)

    def __setitem__(self, key, value):

        print(‘__setitem__:‘,key,value)

    def __delitem__(self, key):

        print(‘__delitem__‘,key)

= Foo()

f["name"= "shuaigaogao"  #自动触发__setitem__方法

f["name"]      #自动触发__getitem__方法

del f["name"]  #自动触发__delitem__方法

#输出

__setitem__: name shuaigaogao

__getitem__: name

__delitem__ name

注:这边的__delitem__没有做真正的删除,只是触发这个方法,想要真正删除,只需要在__delitem__函数中添加删除功能即可

二、类的起源于metaclass

一、概述

  前面我们学习了大篇幅的关于类,通过类创建对象,那我们想知道这个类到底是怎么产生的呢?它的一切来源是什么?还有对象,对象是通过什么方法创建的,现在我们一头雾水,行的,下面我们就来揭开类的面纱,看看类和对象到底是怎么创建的,通过什么创建的。

二、类的起源

2.1 传统创建类


1

2

3

4

5

class Foo(object):

    def __init__(self,name):

        self.name = name

= Foo("shuaigaogao")

f 是通过 Foo 类实例化的对象,其实,不仅 f 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象,按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。


1

2

print(type(f))    #输出:<class ‘__main__.Foo‘>  表示:f 对象由Foo类创建

print(type(Foo))  #输出:<class ‘type‘>          表示:Foo类对象由 type 类创建

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

2.2 type创建类

说明:  type创建类的格式,类名 = type(‘类名‘,(父类,),{‘方法名‘:方法的内存地址})


1

2

3

4

5

6

7

8

9

10

11

12

13

def func(self):  #创建方法

    print("hello {0}".format(self.name))

def __init__(self,name):  #创建构造方法

    self.name = name

#通过type创建类,如果是经典类的话则写成:Foo = type("Foo",(),{"talk":func,"__init__":__init__})

Foo = type("Foo",(object,),{"talk":func,"__init__":__init__}) 

= Foo("shuaigaogao")  #创建对象

f.talk()

#输出

hello shuaigaogao

总结:类 是由 type 类 实例化产生的

值得注意的是,新式类的写法,在继承父类那边,你继承一个父类后面就要加一个逗号,加逗号,它就把它当做一个元组,不加逗号,就是一个值了

三、__new__方法

3.1 概念

new方法是类自带的一个方法,可以重构,__new__方法在实例化的时候也会执行,并且先于__init__方法之前执行


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

class Foo(object):

    def __init__(self,name):

        self.name = name

        print("Foo __init__")

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

        print("Foo __new__",cls*args, **kwargs)

        return object.__new__(cls)

= Foo("shuaigaogao")

#输出

Foo __new__ <class ‘__main__.Foo‘> shuaigaogao  #执行了new方法

Foo __init__  #执行了__init__方法

3.2 new方法作用

作用:所有对象都是通过new方法来实例化的,new里面调用了init方法,所以在实例化的过程中先执行的是new方法,而不是init方法。

①重构__new__方法


1

2

3

4

5

6

7

8

9

10

11

12

13

class Foo(object):

    def __init__(self,name):

        self.name = name

        print("Foo __init__")

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

        print("Foo __new__",cls*args, **kwargs)

= Foo("shuaigaogao")  #实例化

#输出

Foo __new__ <class ‘__main__.Foo‘> shuaigaogao

由上面的例子看出,没有执行__init__方法

②重构__new__方法,并继承父类的__new__方法


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

class Foo(object):

    def __init__(self,name):

        self.name = name

        print("Foo __init__")

    def __new__(cls*args, **kwargs):   #cls相当于传入类Foo

        print("Foo __new__",cls*args, **kwargs)

        return object.__new__(cls)  #继承父类的__new__方法,这边必须以返回值的形式继承

= Foo("shuaigaogao")

#输出

Foo __new__ <class ‘__main__.Foo‘> shuaigaogao

Foo __init__

由上面不难看出,大多数情况下,你都不要去重构你的__new__方法,因为你父类中已经有__new__方法了,已经帮你写好了怎么去创建类,如果你重写的话,就会覆盖父类的里面的__new__方法。但是你重构可以增加一点小功能,但是你覆盖了以后还是需要继承父类回来,要不然你的这个实力就创建不了。

3.3 使用场景

我想对我自己写的一些类进行定制,就在它实例化之前就进行定制,就可以用到__new__方法,new方法就是用来创建实力的,重构new方法,必须以返回值的形式继承父类的new方法。

①需求:我在创建对象时候,同时创建一个类变量


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class Foo(object):

    def __init__(self,name):

        self.name = name

        print("Foo __init__")

    def __new__(cls*args, **kwargs):  #cls相当于是传入的类名Foo

        cls.name = "shuaigaogao"  #创建对象是定义静态变量

        print(cls.name)

        return object.__new__(cls)  #继承父类的__new__方法

= Foo("shuaigaogao")

print(Foo.name)

#输出

shuaigaogao

Foo __init__

shuaigaogao

四、__metaclass__方法

4.1 metaclass作用

metaclass这个属性叫做元类,它是用来表示这个类是由谁来帮他实例化创建的,说白了,就是相当于自己定制一个类,就这么一个意思。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

class MyType(type):

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

        print("Mytype __init__",*args,**kwargs)

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

        print("Mytype __call__"*args, **kwargs)

        obj = self.__new__(self)

        print("obj ",obj,*args, **kwargs)

        print(self)

        self.__init__(obj,*args, **kwargs)

        return obj

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

        print("Mytype __new__",*args,**kwargs)

        return type.__new__(cls*args, **kwargs)

class Foo(object,metaclass=MyType):  #python3统一用这种

    #__metaclass__ = MyType  #python2.7中的写法

    def __init__(self,name):

        self.name = name

        print("Foo __init__")

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

        print("Foo __new__",cls*args, **kwargs)

        return object.__new__(cls)

= Foo("shuaigaogao")

print("f",f)

print("fname",f.name)

#输出

Mytype __new__ Foo (<class ‘object‘>,) {‘__new__‘: <function Foo.__new__ at 0x0000025EF0EFD6A8>,

‘__init__‘: <function Foo.__init__ at 0x0000025EF0EFD620>, ‘__qualname__‘‘Foo‘‘__module__‘‘__main__‘}

Mytype __init__ Foo (<class ‘object‘>,) {‘__new__‘: <function Foo.__new__ at 0x0000025EF0EFD6A8>,

 ‘__init__‘: <function Foo.__init__ at 0x0000025EF0EFD620>, ‘__qualname__‘‘Foo‘‘__module__‘‘__main__‘}

Mytype __call__ shuaigaogao

Foo __new__ <class ‘__main__.Foo‘>

obj  <__main__.Foo object at 0x0000025EF0F05048> shuaigaogao

<class ‘__main__.Foo‘>

Foo __init__

f <__main__.Foo object at 0x0000025EF0F05048>

fname shuaigaogao

创建过程如下:

4.2 执行顺序

类的生成 调用 顺序依次是 __new__ --> __init__ --> __call__

metaclass 详解文章:猛击这里 得票最高那个答案写的非常好

时间: 2024-10-13 06:53:47

面向对象【day08】:类的特殊成员-类的起源与metaclass的相关文章

【Python基础到进阶】类的特殊成员(__xxx__)

类的特殊成员 类的成员可以分为三大类:字段.方法和属性.并且成员名前如果有两个下划线,则表示该成员是私有成员,私有成员只能由类内部调用.无论人或事物往往都有不按套路出牌的情况,Python的类成员也是如此,存在着一些具有特殊含义的成员,详情如下: 1. __doc__ 表示类的描述信息 class Foo: """ 描述类信息,这是用于看片的神奇 """ def func(self): pass print(Foo.__doc__) 2. __m

java面向对象学习之——java中的类修饰符、成员变量修饰符、方法修饰符

类修饰符: public(访问控制符)-->将一个类声明为公共类,他可以被任何对象访问,一个程序的主类必须是公共类.abstract-->将一个类声明为抽象类,没有实现的方法,需要子类提供方法实现.final-->将一个类生命为最终(即非继承类),表示他不能被其他类继承.friendly-->默认的修饰符,只有在相同包中的对象才能使用这样的类. 成员变量修饰符: public(公共访问控制符)-->指定该变量为公共的,他可以被任何对象的方法访问.private(私有访问控制符

Python面向对象-进阶篇(类的特殊成员)

Python类,存在着一些具有特殊含义的成员,详情如下: 1.__doc__ 表示类的描述信息 class Foo: """ 描述类信息,这是用于看片的神奇 """ def func(self): pass print Foo.__doc__ #输出:类的描述信息  2.__module__ 和 __class__ __module__ 表示当前操作的对象在那个模块__class__   表示当前操作的对象的类是什么 from lib.aa im

Python学习:17.Python面向对象(四、属性(特性),成员修饰符,类的特殊成员)

一.属性(特性) 普通方法去执行的时候,后面需要加括号,特性方法执行的时候和静态字段一样不需要不需要加括号. 特性方法不和字段同名. 特性方法不能传参数. 在我们定义数据库字段类的时候,往往需要对其中的类属性做一些限制,一般用get和set方法来写,那在python中,我们该怎么做能够少写代码,又能优雅的实现想要的限制,减少错误的发生呢,这时候就需要我们的@property. 获取特性 class Foo: def __init__(self,name): self.name = name #

面向对象的类的特殊成员

1. __doc__     表示类的描述信息 1 2 3 4 5 6 7 8 9 class Foo:     """ 描述类信息,这是用于看片的神奇 """     def func(self):         pass print Foo.__doc__ ============== 描述类信息,这是用于看片的神奇 2. __module__ 和  __class__  __module__ 表示当前操作的对象在哪个模块    __cla

sdut 面向对象程序设计上机练习七(类和对象)

面向对象程序设计上机练习七(类和对象) Time Limit: 1000MS Memory limit: 65536K 题目描述 利用类的数据成员和成员函数完成下列操作:输入三个整数,输出它们的最大值. 输入 输入三个整数. 输出 输出3个整数的最大值. 示例输入 2 8 5 示例输出 8 这个就不做详细注释了,初学者也能尝试着看懂. #include <iostream> using namespace std; class shu { private: int a,b,c; public:

(继承及其访问限定符)&&(派生类及其默认成员函数)&&(赋值兼容规则)

◆继承: ★继承概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能.这样产生新的类,称派生类.继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程. 继承定义格式 ★继承关系&访问限定符 class Base { public: Base() { cout<<"B()" <<endl; } ~Base () { cout<<"~

C++ Primer 学习笔记_65_面向对象编程 --概述、定义基类和派生类

面向对象编程 --概述.定义基类和派生类 引言: 面向对象编程基于的三个基本概念:数据抽象.继承和动态绑定. 在C++中,用类进行数据抽象,用类派生从一个类继承另一个:派生类继承基类的成员.动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数. 继承和动态绑定在两个方面简化了我们的程序:[继承]能够容易地定义与其他类相似但又不相同的新类,[派生]能够更容易地编写忽略这些相似类型之间区别的程序. 面向对象编程:概述 面向对象编程的关键思想是多态性(polymorphism)

Lesson 04:类和对象,类的成员变量、成员方法、构造方法

1 类(class) java是面向对象程序设计(OPP),类是构造对象的模板或者蓝图.由类构造(construst)对象的过程称为创建类的实例(instance).类的构成: 1 package 包名 2 class 类名 extends 父类名 implements 接口名{ 3 成员变量: 4 构造方法: 5 成员方法: 6 } 7 //一个简化的类的结构 成员变量 成员方法:包含返回值类型.参数列表.方法主体等要素 (1)方法的声明: 1 //方法的声明没有函数体 2 public in