类成员的修饰符
类的所有成员在上一篇已经做了详细的介绍,对于每一个类的成员都有两种形式:公有成员、私有成员。成员定义是以双下划线开头,就是私有成员。除了一些特殊成员除外,例如:__init__、__call__、__dict__、__del__等,剩下的都是公有成员。
class Base:
def __init__(self):
self.name = '公有字段'
self.__foo = "私有字段"
私有成员和公有成员的访问级别不同:
普通字段
- 公有普通字段:对象可以访问;类内部可以访问;派生类中可以访问
- 私有普通字段:仅类内部可以访问;
私有字段其实不是不能访问,只是Python解释器对私有成员命名做了更改,对象._类名__私有字段名,例如:obj._Base__foo
不建议强制访问私有成员。
普通公有字段例子
class Base:
def __init__(self):
self.foo = '公有字段'
def func(self):
print(self.foo) # 类内部访问
class Device(Base):
def show(self):
print(self.foo) # 派生类中访问
obj = Base()
print(obj.foo) # 对象访问
obj.func() # 类内部访问
obj_son = Device()
obj_son.show() # 派生类访问
普通私有字段例子
class Base:
def __init__(self):
self.__foo = '私有字段'
def func(self):
print(self.__foo) # 类内部访问
class Device(Base):
def show(self):
print(self.__foo) # 派生类中访问
obj = Base()
print(obj.__foo) # 对象访问 报错:AttributeError: 'Base' object has no attribute '__foo'
obj.func() # 类内部访问
obj_son = Device()
obj_son.show() # 派生类访问 报错:AttributeError: 'Device' object has no attribute '_Device__foo'
方法、属性的访问都是相似的,即:私有成员只能在类内部使用
静态字段
- 公有静态字段:类可以访问;类内部可以访问;派生类中可以访问
- 私有静态字段:仅类内部可以访问;
静态公有字段例子
class Base:
name = "公有静态字段"
def func(self):
print(Base.name)
class Device(Base):
def show(self):
print(Base.name)
Base.name # 类访问
obj = Base()
obj.func() # 类内部可以访问
obj_son = Device()
obj_son.show() # 派生类中可以访问
静态私有字段例子
class Base:
__name = "公有静态字段"
def func(self):
print(Base.__name)
class Device(Base):
def show(self):
print(Base.__name)
Base.__name # 类访问 报错:AttributeError: type object 'Base' has no attribute '__name'
obj = Base()
obj.func() # 类内部访问
obj_son = Device()
obj_son.show() # 派生类访问 报错:AttributeError: type object 'Base' has no attribute '_Device__name'
通过上面的例子,你应该发现了一个问题,在普通公有字段中,子类可以通过self.foo访问父类定义的变量;在静态公有字段中,使用的是Base.name访问。那为什么不能使用self访问呢?上一篇我们其实讲过的,静态字段属于类所有,在类中只保存一份,所以它与继承无关,不管经过多少重继承,静态字段只有一份,只能通过类自身来访问。而普通字段是属于对象的,所以继承以后,每一个继承类的对象都会保存一份。
类的特殊成员
上面我们讲了类成员以及成员修饰符,知道了类中有字段、方法和属性,并且有公有和私有两种访问限制。但是还是存在着一些具有特殊含义的成员,详情如下:
- __doc__
表示类的描述信息
库函数:range
class range(object):
"""
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
"""
def count(self, value): # real signature unknown; restored from __doc__
""" rangeobject.count(value) -> integer -- return number of occurrences of value """
return 0
print(range.__doc__)
#输出结果:
range(stop) -> range object
range(start, stop[, step]) -> range object
Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step. range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted! range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).
当我们在类上面加上一些说明,通过此方法可以进行查看,同样也可以查看库函数的说明
- __module__ 和 class
__module__ 表示当前操作的对象在那个模块
__class__ 表示当前操作的对象的类是什么
class Foo:
def func(self):
pass
f = Foo()
print(f.__module__) #输出:__main__
print(f.__class__) #输出:<class '__main__.Foo'>
from multiprocessing import Process
p = Process()
print(p.__module__) #输出:multiprocessing.context
print(p.__class__) #输出:<class 'multiprocessing.context.Process'>
- __init__
构造方法,通过类创建对象时,自动触发执行。
class Foo:
def __init__(self, name):
self.name = name
self.age = 18
obj = Foo('wupeiqi') # 自动执行类中的 __init__ 方法
注意:Python这里的构造方法与C++不一样
class Foo:
instance = None
def __init__(self):
print("__init__")
@classmethod
def __new__(cls, *args, **kwargs):
if not cls.instance:
cls.instance = object.__new__(cls)
print("__new__")
return cls.instance
f1 = Foo()
f2 = Foo()
print(id(f1), id(f2))
#输出:
__new__
__init__
__init__
4343767560 4343767560
可以看到,f1和f2是同一个对象,占据同一块内存,也就是说在内存中只创建了一个对象,但是构造函数调用了两次。所以这里的创建对象时,自动触发并不是特别准确,使用的时候要多注意。
- __del__
析构方法,当对象在内存中被释放时,自动触发执行。
此方法一般无须定义,因为Python是一门高级语言,程序员在使用时不需要关心内存的分配和释放,因为都是交给Python解释器来执行,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
def __del__(self):
print("del")
还是上面那个例子,加上__del__函数的定义
输出:
__new__
__init__:
__init__:
4424543312 4424543312
del
可以看到del也是调用了一次,再次证实了,对象只创建了一次
- __call__
对象后面加括号,触发执行,相当于函数调用。
构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 call 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print '__call__'
obj = Foo() # 执行 __init__
obj() # 执行 __call__
- __dict__
类或对象中的所有成员
上文中我们知道:类的普通字段属于对象;类中的静态字段和方法等属于类,即:
class Foo:
instance = None
def __init__(self, name):
print("__init__: ", name)
self.name = name
def func(self):
pass
f1 = Foo("name1")
# 获取对象的成员,即:普通字段
print(f1.__dict__) # 输出:{'name': 'name1', 'age': 123}
# 获取类的成员,即:静态字段、方法、
print(Foo.__dict__) # 输出:{'__module__': '__main__', 'instance': None, '__init__': <function Foo.__init__ at 0x10df509d8>, 'func': <function Foo.func at 0x1151b79d8>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
- __str__
如果一个类中定义了__str__方法,那么在打印对象或者str转换时,默认输出该方法的返回值。
class Foo:
def __str__(self):
return 'foo_name'
obj = Foo()
print(obj)
# 输出:foo_name
print(str(obj))
# 输出:foo_name
- __getitem__、__setitem__、__delitem__
用于索引操作,如字典、列表。以上分别表示获取、设置、删除数据
字典
class Foo:
def __getitem__(self, item):
print("__getitem__: ", item)
def __setitem__(self, key, value):
print("__setitem__: ", key, value)
def __delitem__(self, key):
print("__delitem__: ", key)
obj = Foo()
result = obj["k1"] # 自动触发__getitem__
obj["k2"] = "name" # 自动触发__setitem__
del obj["k1"] # 自动触发__delitem__
列表
class Foo:
def __getitem__(self, item):
print("__getitem__.start: ", item.start)
print("__getitem__.stop: ", item.stop)
print("__getitem__.step: ", item.step)
def __setitem__(self, key, value):
print("__setitem__.index: ", key)
print("__setitem__.value: ", value)
return value
def __delitem__(self, key):
print("__delitem__: ", key)
f = Foo()
f[1:5:2] #自动触发__getitem__
f[0] = 1 #自动触发__setitem__
del f[0] #自动触发__delitem__
#输出
__getitem__.start: 1
__getitem__.stop: 5
__getitem__.step: 2
__setitem__.index: 0
__setitem__.value: 1
__delitem__: 0
- __iter__
用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 iter
class Foo:
def __init__(self, sq):
self.sq = sq
def __iter__(self):
return iter(self.sq)
obj = Foo([11,22,33,44])
for i in obj:
print(i)
for循环迭代的其实是 iter([11,22,33,44]) ,所以相当于如下:
obj = iter([11,22,33,44])
for i in obj:
print(i)
For循环迭代器输出
obj = iter([11,22,33,44])
while True:
try:
val = next(obj)
print(val)
except StopIteration as e:
break
还有__metaclass__也是非常重要的一个,这个牵扯到反射机制,我们放在下一篇进行详细说明,今天的文章就到这里了,你有没有Get到新技能呢?欢迎留言一起探讨