四时宝库

程序员的知识宝库

深入理解Python面向对象-类特殊成员

接上一篇 深入理解Python面向对象-类成员

类成员的修饰符

类的所有成员在上一篇已经做了详细的介绍,对于每一个类的成员都有两种形式:公有成员私有成员。成员定义是以双下划线开头,就是私有成员。除了一些特殊成员除外,例如:__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访问呢?上一篇我们其实讲过的,静态字段属于类所有,在类中只保存一份,所以它与继承无关,不管经过多少重继承,静态字段只有一份,只能通过类自身来访问。而普通字段是属于对象的,所以继承以后,每一个继承类的对象都会保存一份。

类的特殊成员

上面我们讲了类成员以及成员修饰符,知道了类中有字段、方法和属性,并且有公有和私有两种访问限制。但是还是存在着一些具有特殊含义的成员,详情如下:

  1. __doc__
  2. 表示类的描述信息

库函数: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).

当我们在类上面加上一些说明,通过此方法可以进行查看,同样也可以查看库函数的说明

  1. __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'>
  1. __init__
  2. 构造方法,通过类创建对象时,自动触发执行。
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是同一个对象,占据同一块内存,也就是说在内存中只创建了一个对象,但是构造函数调用了两次。所以这里的创建对象时,自动触发并不是特别准确,使用的时候要多注意。

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

此方法一般无须定义,因为Python是一门高级语言,程序员在使用时不需要关心内存的分配和释放,因为都是交给Python解释器来执行,所以析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

 def __del__(self):
 print("del")

还是上面那个例子,加上__del__函数的定义

输出:

__new__
__init__: 
__init__: 
4424543312 4424543312
del

可以看到del也是调用了一次,再次证实了,对象只创建了一次

  1. __call__
  2. 对象后面加括号,触发执行,相当于函数调用。

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

class Foo:
 def __init__(self):
 pass

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

obj = Foo() # 执行 __init__
obj() # 执行 __call__
  1. __dict__
  2. 类或对象中的所有成员

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

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}
  1. __str__
  2. 如果一个类中定义了__str__方法,那么在打印对象或者str转换时,默认输出该方法的返回值。
class Foo:
 def __str__(self):
 return 'foo_name'

obj = Foo()
print(obj)
# 输出:foo_name

print(str(obj)) 
# 输出:foo_name
  1. __getitem__、__setitem__、__delitem__
  2. 用于索引操作,如字典、列表。以上分别表示获取、设置、删除数据
  3. 字典
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
  1. __iter__
  2. 用于迭代器,之所以列表、字典、元组可以进行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到新技能呢?欢迎留言一起探讨

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接