一、定义
类是一种对象,与其它语言一样,所有的对象都属于某一个类,称为类的实例(instance)。python中类名习惯用单数名词表示,且首字母大写。对象可以看做是数据(特性)以及由一系列可以存取、操作这些数据的方法所组成的集合。对象的优点:
1、多态(polymorphism):意味着可以对不同的类的对象使用同样的操作,它们会像被“施了魔法一般”工作。
2、封装(encapsulation):对外部世界隐藏对象的工作细节。
3、继承(inheritance):以通用的类为基础建立专门的类对象。
二、类的数据属性
在Python中,可以通过class关键字定义自己的类,然后通过自定义的类对象类创建实例对象。
例如,下面创建了一个Student的类,并且实现了这个类的初始化函数"__init__":
>>> class Student:
count = 0
books = []
def __init__(self,name,age):
self.name = name
self.age = age
>>> dir(Student)
['__doc__', '__init__', '__module__', 'books', 'count']
>>> Student.__bases__
()
>>>
继承于object类(基类放在圆里面):
>>> class Student(object):
'''
this is a Student class.
'''
count = 0
books = []
def __init__(self,name,age):
self.name = name
self.age = age
pass
>>> dir(Student)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'books', 'count']
>>> print Student.__name__
Student
>>> print Student.__doc__
this is a Student class.
>>> print Student.__bases__
(<type 'object'>,)
>>> print Student.__dict__
{'count': 0, '__module__': '__main__', 'books': [], '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': '\n this is a Student class.\n ', '__init__': <function __init__ at 0x01AF38F0>}
>>> print Student.__class__
<type 'type'>
>>>
1、特殊的类属性
对于所有的类,都有一组特殊的属性:
类属性 | 含义 |
__name__ | 类的名字(字符串) |
__doc__ | 类的文档字符串 |
__bases__ | 类的所有父类组成的元组 |
__dict__ | 类的属性组成的字典 |
__module__ | 类所属的模块 |
__class__ | 类对象的类型 |
接下来就通过上面的Student类来看看Python中类的相关内容。
在上面的Student类中,"count""books""name"和"age"都被称为类的数据属性,但是它们又分为类数据属性和实例数据属性。
2、类数据属性和实例数据属性
首先看一段代码,代码中分别展示了对类数据属性和实例数据属性的访问:
>>> Student.books.extend(['python','C++'])
>>> print 'Student book list: %s' %Student.books
Student book list: ['python', 'C++']
>>> #class can add class attribute after class defination
>>> Student.hobbies = ['reading','jogging','swimming']
>>> print "Student's hobby list: %s" %Student.hobbies
Student's hobby list: ['reading', 'jogging', 'swimming']
>>> print dir(Student)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'books', 'count', 'hobbies']
>>>
>>>
>>> wilber = Student('Wilber',28)
>>> print '%s is %d years old.' %(wilber.name,wilber.age)
Wilber is 28 years old.
>>> #class instance can add new attribute.
>>> #"gender" is the instance attribute only belongs to wilber.
>>> wilber.gender = 'male'
>>> print '%s is %s.' %(wilber.name,wilber.gender)
Wilber is male.
>>> print dir(wilber)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'books', 'count', 'gender', 'hobbies', 'name']
>>>
>>> print wilber.books
['python', 'C++']
>>> wilber.books.append('C#')
>>> print wilber.books
['python', 'C++', 'C#']
>>>
>>> will = Student('Will',29)
>>> print '%s is %d years old.' %(will.name,will.age)
Will is 29 years old.
>>> #will shares the same class attribute with wilber.
>>> #will don't have the "gender" attribute that belongs to wilber.
>>> print dir(will)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'books', 'count', 'hobbies', 'name']
>>> print will.books
['python', 'C++', 'C#']
>>>
通过内建函数dir(),或者访问类的字典属性__dict__,这两种方式都可以查看类有哪些属性:
对于类数据属性和实例数据属性,可以总结为:
- 类数据属性属于类本身,可以通过类名进行访问、修改。
- 类数据属性也可以被类的所有实例访问、修改。
- 在类定义之后,可以通过类名动态添加类数据属性,新增的类属性也被类和所有实例共有。
- 实例数据属性只能通过实例访问。
- 在实例生成后,还可以动态添加实例数据属性,但是这些实例数据属性只属于该实例。
3、属性隐藏
从上面的介绍了解到,类数据属性属于类本身,被所有该类的实例共享;并且,通过实例可以去访问/修改类属性。但是,在通过实例中访问类属性的时候一定要谨慎,因为可能出现属性"隐藏"的情况。
继续使用上面的Student类,来看看属性隐藏:
>>> wilber
<__main__.Student object at 0x01C87150>
>>> print 'Student.count is wilber.count:',Student.count is wilber.count
Student.count is wilber.count: True
>>> wilber.count = 1
>>> print 'Student.count is wilber.count:',Student.count is wilber.count
Student.count is wilber.count: False
>>> print Student.__dict__
{'count': 0, '__module__': '__main__', 'books': ['python', 'C++', 'C#'], 'hobbies': ['reading', 'jogging', 'swimming'], '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': '\n this is a Student class.\n ', '__init__': <function __init__ at 0x01C838F0>}
>>> print wilber.__dict__
{'count': 1, 'gender': 'male', 'age': 28, 'name': 'Wilber'}
>>>
>>> del wilber.count
>>> print 'Student.count is wilber.count:',Student.count is wilber.count
Student.count is wilber.count: True
>>>
>>> wilber.count += 3
>>> print 'Student.count is wilber.count:',Student.count is wilber.count
Student.count is wilber.count: False
>>> print Student.__dict__
{'count': 0, '__module__': '__main__', 'books': ['python', 'C++', 'C#'], 'hobbies': ['reading', 'jogging', 'swimming'], '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': '\n this is a Student class.\n ', '__init__': <function __init__ at 0x01C838F0>}
>>> print wilber.__dict__
{'count': 3, 'gender': 'male', 'age': 28, 'name': 'Wilber'}
>>> del wilber.count
>>>
>>>
>>> print 'Student.books is wilber.books:',Student.books is wilber.books
Student.books is wilber.books: True
>>> print wilber.books
['python', 'C++', 'C#']
>>> wilber.books = ['C++','C#']
>>> print 'Student.books is wilber.books:',Student.books is wilber.books
Student.books is wilber.books: False
>>> print Student.__dict__
{'count': 0, '__module__': '__main__', 'books': ['python', 'C++', 'C#'], 'hobbies': ['reading', 'jogging', 'swimming'], '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': '\n this is a Student class.\n ', '__init__': <function __init__ at 0x01C838F0>}
>>> print wilber.__dict__
{'gender': 'male', 'age': 28, 'books': ['C++', 'C#'], 'name': 'Wilber'}
>>> del wilber.books
>>> print 'Student.books is wilber.books:',Student.books is wilber.books
Student.books is wilber.books: True
>>>
>>>
>>> wilber.books.append('CSS')
>>> print 'Student.books is wilber.books:',Student.books is wilber.books
Student.books is wilber.books: True
>>> print Student.__dict__
{'count': 0, '__module__': '__main__', 'books': ['python', 'C++', 'C#', 'CSS'], 'hobbies': ['reading', 'jogging', 'swimming'], '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': '\n this is a Student class.\n ', '__init__': <function __init__ at 0x01C838F0>}
>>> print wilber.__dict__
{'gender': 'male', 'age': 28, 'name': 'Wilber'}
>>>
分析一下上面代码的输出:
- 对于不可变类型的类属性Student.count,可以通过实例wilber进行访问,并且"Student.count is wilber.count"
- 当通过实例赋值/修改count属性的时候,都将为实例wilber新建一个count实例属性,这时,"Student.count is not wilber.count"
-
当通过"del wilber.count"语句删除实例的count属性后,再次成为 "Student.count is wilber.count"
- 同样对于可变类型的类属性Student.books,可以通过实例wilber进行访问,并且"Student. books is wilber. books"
- 当通过实例赋值books属性的时候,都将为实例wilber新建一个books实例属性,这时,"Student. Books is not wilber. books"
- 当通过"del wilber. books"语句删除实例的books属性后,再次成为"Student. books is wilber. books"
- 当通过实例修改books属性的时候,将修改wilber.books指向的内存地址(即Student.books),此时,"Student. Books is wilber.books"
注意,虽然通过实例可以访问类属性,但是,不建议这么做,最好还是通过类名来访问类属性,从而避免属性隐藏带来的不必要麻烦。
三、类的方法
在一个类中,可能出现三种方法,实例方法、静态方法和类方法,下面来看看三种方法的不同。
1、实例方法
实例方法的第一个参数必须是"self","self"类似于C++中的"this"。
实例方法只能通过类实例进行调用,这时候"self"就代表这个类实例本身。通过"self"可以直接访问实例的属性。
>>> class Student(object):
'''
this is a Student class.
'''
count = 0
books = []
def __init__(self,name,age):
self.name = name
self.age = age
def printInstanceInfo(self):
print '%s is %d years old' %(self.name,self.age)
pass
>>> wilber = Student('Wilber',28)
>>> wilber.printInstanceInfo()
Wilber is 28 years old
>>> Student.printInstanceInfo()
Traceback (most recent call last):
File "<pyshell#19>", line 1, in <module>
Student.printInstanceInfo()
TypeError: unbound method printInstanceInfo() must be called with Student instance as first argument (got nothing instead)
2、类方法
类方法以cls作为第一个参数,cls表示类本身,定义时使用@classmethod装饰器。通过cls可以访问类的相关属性。
>>> class Student(object):
'''
This is a Student class.
'''
count = 0
books = []
def __init__(self,name,age):
self.name = name
self.age = age
@classmethod
def printClassInfo(cls):
print cls.__name__
print dir(cls)
pass
>>> Student.printClassInfo()
Student
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'books', 'count', 'printClassInfo']
>>> Wilber = Student('Wilber',28)
>>> Wilber.printClassInfo()
Student
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'books', 'count', 'printClassInfo']
从这段代码可以看到,类方法可以通过类名访问,也可以通过实例访问。
3、静态方法
与实例方法和类方法不同,静态方法没有参数限制,既不需要实例参数,也不需要类参数,定义的时候使用@staticmethod装饰器。
同类方法一样,静态法可以通过类名访问,也可以通过实例访问。
>>> class Student(object):
'''
This is a Student class.
'''
count = 0
books = []
def __init__(self,name,age):
self.name = name
self.age = age
@staticmethod
def printClassAttr():
print Student.__name__
print Student.count
print Student.books
pass
>>> Student.printClassAttr()
Student
0
[]
>>> wilber = Student('Wilber',28)
>>> wilber.printClassAttr()
Student
0
[]
>>>
这三种方法的主要区别在于参数,实例方法被绑定到一个实例,只能通过实例进行调用;但是对于静态方法和类方法,可以通过类名和实例两种方式进行调用。
四、类的访问控制
Python中没有访问控制的关键字,例如private、protected等等。但是,在Python编码中,有一些约定来进行访问控制。
1、单下划线"_"
在Python中,通过单下划线"_"来实现模块级别的私有化,一般约定以单下划线"_"开头的变量、函数为模块私有的,也就是说"from moduleName import *"将不会引入以单下划线"_"开头的变量、函数。
现在有一个模块lib.py,内容用如下,模块中一个变量名和一个函数名分别以"_"开头:
numA = 10 _numA = 100 def printNum(): print "numA is:", numA print "_numA is:", _numA def _printNum(): print "numA is:", numA print "_numA is:", _numA
当通过下面代码引入lib.py这个模块后,所有的以"_"开头的变量和函数都没有被引入,如果访问将会抛出异常:
from lib import * print numA printNum() print _numA #print _printNum()
2、双下划线"__"
对于Python中的类属性,可以通过双下划线"__"来实现一定程度的私有化,因为双下划线开头的属性在运行时会被"混淆"(mangling)。
在Student类中,加入了一个"__address"属性:
>>> class Student(object):
def __init__(self,name,age):
self.name = name
self.age = age
self.__address = 'shenzhen'
pass
>>> wilber = Student('Wilber',28)
>>> print wilber.name
Wilber
>>> print wilber.__address
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
print wilber.__address
AttributeError: 'Student' object has no attribute '__address'
>>>
当通过实例wilber访问这个属性的时候,就会得到一个异常,提示属性"__address"不存在。
其实,通过内建函数dir()就可以看到其中的一些原由,"__address"属性在运行时,属性名被改为了"_Student__address"(属性名前增加了单下划线和类名)
>>> dir(wilber)
['_Student__address', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name']
>>>
所以说,即使是双下划线,也没有实现属性的私有化,因为通过下面的方式还是可以直接访问"__address"属性:
>>> print wilber._Student__address
shenzhen
>>>
双下划线的另一个重要的目地是,避免子类对父类同名属性的冲突。
看下面一个例子:
>>> class A(object):
def __init__(self):
self.__private()
self.public()
def __private(self):
print 'A.private()'
def public(self):
print 'A.public()'
self.__private()
>>> class B(A):
def __private(self):
print 'B.__private()'
def public(self):
print 'B.public()'
self.__private()
>>> a = A()
A.private()
A.public()
A.private()
>>> b = B()
A.private()
B.public()
B.__private()
>>> #A的实例a无法调用私有的__private(self)
>>> a.__private()
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
a.__private()
AttributeError: 'A' object has no attribute '__private'
>>> #但A的公有的内部函数可以
>>> a.public()
A.public()
A.private()
>>> b.__private()
Traceback (most recent call last):
File "<pyshell#12>", line 1, in <module>
b.__private()
AttributeError: 'B' object has no attribute '__private'
>>> b.public()
B.public()
B.__private()
>>> b._B__private()
B.__private()
>>> #但是,通过“翻译”成前面加上单下划线和类名的形式就可以。
>>> a._A__private()
A.private()
>>>
当实例化B的时候,由于没有定义__init__函数,将调用父类的__init__,但是由于双下划线的"混淆"效果,"self.__private()"将变成 "self._A__private()"。
"_"和" __"的使用 更多的是一种规范/约定,不没有真正达到限制的目的:
- "_":以单下划线开头的表示的是protected类型的变量,即只能允许其本身与子类进行访问;同时表示弱内部变量标示,如,当使用"from moduleNmae import *"时,不会将以一个下划线开头的对象引入。
- "__":双下划线像其它语言中的标准的私有方法。只能是允许这个类内部,但连该类的实例也没权限访问(其它语言是可以的),更不用说子类,所有以双下划线开始的名字都被“翻译”成前面加上单下划线和类名的形式。
- 简而言之,确保其他人不会访问对象的方法和特性是不可能的,但是这类“名称变化术”就是他们不应该访问这些函数或者特性的强有力信号。
本文将介绍一下类的构造函数和初始化函数,以及如何通过"魔术方法"定制一个类。
五、类的构造和初始化
在前面的文章中,经常使用初始化函数"__init__",下面看看"__init__"和"__new__"的联系和差别。
下面先通过一段代码看看这两个方法的调用顺序:
>>> class A(object):
def __init__(self,*args,**kwiargs):
print "init %s" %self.__class__
def __new__(cls,*args,**kwiargs):
print 'new %s' %cls
return object.__new__(cls,*args,**kwiargs)
>>> a = A()
new <class '__main__.A'>
init <class '__main__.A'>
>>>
从代码的输出可以看到,当通过类实例化一个对象的时候,"__new__"方法首先被调用,然后是"__init__"方法。
一般来说,"__init__"和"__new__"函数都会有下面的形式:
def __init__(self, *args, **kwiargs):
# func_suite
def __new__(cls, *args, **kwiargs):
# func_suite
return obj
对于"__new__"和"__init__"可以概括为:
- "__new__"方法在Python中是真正的构造方法(创建并返回实例),通过这个方法可以产生一个"cls"对应的实例对象,所以说"__new__"方法一定要有返回。
- 对于"__init__"方法,是一个初始化的方法,"self"代表由类产生出来的实例对象,"__init__"将对这个对象进行相应的初始化操作。
前面文章中已经介绍过了"__init__"的一些行为,包括继承情况中"__init__"的表现。下面就重点看看"__new__"方法。
1、__new__特性
"__new__"是在新式类中新出现的方法,它有以下行为特性:
- "__new__" 方法是在类实例化对象时第一个调用的方法,将返回实例对象。
- "__new__" 方法始终都是类的静态方法(即第一个参数为cls),即使没有被加上静态方法装饰器。
- 第一个参数cls是当前正在实例化的类,如果要得到当前类的实例,应当在当前类中的 "__new__" 方法语句中调用当前类的父类的" __new__" 方法。
对于上面的第三点,如果当前类是直接继承自 object,那当前类的 "__new__" 方法返回的对象应该为:
def __new__(cls, *args, **kwiargs):
# func_suite
return object.__new__(cls, *args, **kwiargs)
2、重写__new__
如果(新式)类中没有重写"__new__"方法,Python默认是调用该类的直接父类的"__new__"方法来构造该类的实例,如果该类的父类也没有重写"__new__",那么将一直按照同样的规则追溯至object的"__new__"方法,因为object是所有新式类的基类。
而如果新式类中重写了"__new__"方法,那么可以选择任意一个其它的新式类(必须是新式类,只有新式类有"__new__",因为所有新式类都是从object派生)的"__new__"方法来创建实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死循环。
看一段例子代码:
>>> class Foo(object):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
# 这里的object.__new__(cls, *args, **kwargs) 等价于
# super(Foo, cls).__new__(cls, *args, **kwargs)
# object.__new__(Foo, *args, **kwargs)
# Bar.__new__(cls, *args, **kwargs)
# Student.__new__(cls, *args, **kwargs),即使Student跟Foo没有关系,也是允许的,因为Student是从object派生的新式类
# 在任何新式类,不能调用自身的“__new__”来创建实例,因为这会造成死循环
# 所以要避免return Foo.__new__(cls, *args, **kwargs)或return cls.__new__(cls, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj
>>> class Bar(Foo):
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj
>>> class Student(object):
# Student没有“__new__”方法,那么会自动调用其父类的“__new__”方法来创建实例,即会自动调用 object.__new__(cls)
pass
>>> class Car(object):
def __new__(cls, *args, **kwargs):
# 可以选择用Bar来创建实例
obj = object.__new__(Bar, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj
>>> foo = Foo()
Call __new__ for <class '__main__.Foo'>
>>> bar = Bar()
Call __new__ for <class '__main__.Bar'>
>>> car = Car()
Call __new__ for <class '__main__.Bar'>
>>>
3、__init__的调用
"__new__"决定是否要使用该类的"__init__"方法,因为"__new__" 可以调用其他类的构造方法或者直接返回别的类创建的对象来作为本类的实例。
通常来说,新式类开始实例化时,"__new__"方法会返回cls(cls指代当前类)的实例,然后调用该类的"__init__"方法作为初始化方法,该方法接收这个实例(即self)作为自己的第一个参数,然后依次传入"__new__"方法中接收的位置参数和命名参数。
但是,如果"__new__"没有返回cls(即当前类)的实例,那么当前类的"__init__"方法是不会被调用的。看下面的例子:
>>> class A(object):
def __init__(self, *args, **kwargs):
print "Call __init__ from %s" %self.__class__
def __new__(cls, *args, **kwargs):
obj = object.__new__(cls, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj
>>> class B(object):
def __init__(self, *args, **kwargs):
print "Call __init__ from %s" %self.__class__
def __new__(cls, *args, **kwargs):
obj = object.__new__(A, *args, **kwargs)
print "Call __new__ for %s" %obj.__class__
return obj
>>> b = B()
Call __new__ for <class '__main__.A'>
>>>
代码中,在B的"__new__"方法中,通过"obj = object.__new__(A, *args, **kwargs)"创建了一个A的实例,在这种情况下,B的"__init__"函数就不会被调用到。
4、派生不可变类型
关于"__new__"方法还有一个重要的用途就是用来派生不可变类型。
例如,Python中float是不可变类型,如果想要从float中派生一个子类,就要实现"__new__"方法:
>>> class Round2Float(float):
def __new__(cls, num):
num = round(num, 2)
#return super(Round2Float, cls).__new__(cls, num)
return float.__new__(Round2Float, num)
>>> f = Round2Float(4.14159)
>>> print f
4.14
>>>
代码中从float派生出了一个Round2Float类,该类的实例就是保留小数点后两位的浮点数。
六、定制一个类
在Python中,我们可以通过"魔术方法"使自定义的class变得强大、易用。
例如,前面的文章中介绍过Python迭代器,当我们想定义一个可迭代的类对象的时候,就可以去实现"__iter__(self)"这个魔术方法;
又例如,前面文章介绍的上下文管理器,当需要建立一个上下文管理器类对象的时候,就可以去实现"__enter__(self)"和"__exit__(self)"方法。
所以,建议参考 "魔术方法" 的文档,通过魔术方法来定制自定义的类。
1、调用魔术方法
一些魔术方法直接和内建函数相对应的,在这种情况下,调用他们的方法很简单,下面给出了一些对应表。
魔术方法 | 调用方式 | 解释 |
__new__(cls [,...]) | instance = MyClass(arg1, arg2) | __new__ 在创建实例的时候被调用 |
__init__(self [,...]) | instance = MyClass(arg1, arg2) | __init__ 在创建实例的时候被调用 |
__cmp__(self, other) | self == other, self > other, 等。 | 在比较的时候调用 |
__pos__(self) | +self | 一元加运算符 |
__neg__(self) | -self | 一元减运算符 |
__invert__(self) | ~self | 取反运算符 |
__index__(self) | x[self] | 对象被作为索引使用的时候 |
__nonzero__(self) | bool(self) | 对象的布尔值 |
__getattr__(self, name) | self.name # name 不存在 | 访问一个不存在的属性时 |
__setattr__(self, name, val) | self.name = val | 对一个属性赋值时 |
__delattr__(self, name) | del self.name | 删除一个属性时 |
__getattribute(self, name) | self.name | 访问任何属性时 |
__getitem__(self, key) | self[key] | 使用索引访问元素时 |
__setitem__(self, key, val) | self[key] = val | 对某个索引值赋值时 |
__delitem__(self, key) | del self[key] | 删除某个索引值时 |
__iter__(self) | for x in self | 迭代时 |
__contains__(self, value) | value in self, value not in self | 使用 in 操作测试关系时 |
__concat__(self, value) | self + other | 连接两个对象时 |
__call__(self [,...]) | self(args) | "调用"对象时 |
__enter__(self) | with self as x: | with 语句环境管理 |
__exit__(self, exc, val, trace) | with self as x: | with 语句环境管理 |
__getstate__(self) | pickle.dump(pkl_file, self) | 序列化 |
__setstate__(self) | data = pickle.load(pkl_file) | 序列化 |
七、总结
本文介绍了Python中class的一些基本点:
- 实例数据属性和类数据属性的区别,以及属性隐藏
- 实例方法,类方法和静态方法直接的区别
- Python中通过"_"和"__"实现的访问控制
文中介绍了类的构造和初始化方法:"__new__"和"__init__"。
"__new__"方法是新式类特有的方法,通常情况下,__new__方法会创建返回cls(cls指代当前类)的实例,然后调用该类的"__init__"方法作为初始化方法,该方法接收这个实例(即self)作为自己的第一个参数,然后依次传入"__new__"方法中接收的位置参数和命名参数;但是,如果"__new__"没有返回cls(即当前类)的实例,那么当前类的"__init__"方法是不会被调用的。
通过"魔术方法",可以对自定义类进行定制、扩展,使得自定义类变得强大、易用。