前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解Python面向对象-类特殊成员

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

作者头像
星星在线
发布2019-05-31 17:41:28
4800
发布2019-05-31 17:41:28
举报

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

类成员的修饰符

类的所有成员在上一篇已经做了详细的介绍,对于每一个类的成员都有两种形式:公有成员私有成员。成员定义是以双下划线开头,就是私有成员。除了一些特殊成员除外,例如:__init__、__call__、__dict__、__del__等,剩下的都是公有成员。

代码语言:javascript
复制
class  Base:
    def  __init__(self):
        self.name = '公有字段'
        self.__foo = "私有字段"

私有成员和公有成员的访问级别不同:

普通字段

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

私有字段其实不是不能访问,只是Python解释器对私有成员命名做了更改,对象.类名_私有字段名,例如:obj._Base__foo不建议强制访问私有成员。

普通公有字段例子

代码语言:javascript
复制
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()      # 派生类访问

普通私有字段例子

代码语言:javascript
复制
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'

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

静态字段

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

静态公有字段例子

代码语言:javascript
复制
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() # 派生类中可以访问

静态私有字段例子

代码语言:javascript
复制
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__ 表示类的描述信息

库函数:range

代码语言:javascript
复制
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 表示当前操作的对象的类是什么
代码语言:javascript
复制
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__   构造方法,通过类创建对象时,自动触发执行。
代码语言:javascript
复制
class  Foo:
     def  __init__(self, name):
        self.name = name
        self.age = 18

obj = Foo('wupeiqi') # 自动执行类中的 __init__ 方法

注意:Python这里的构造方法与C++不一样

代码语言:javascript
复制
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__   析构方法,当对象在内存中被释放时,自动触发执行。

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

代码语言:javascript
复制
    def  __del__(self):
        print("del")

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

代码语言:javascript
复制
__new__
__init__: 
__init__: 
4424543312 4424543312
del

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

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

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

代码语言:javascript
复制
class  Foo:
    def  __init__(self):
        pass

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

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

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

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

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

print(str(obj)) 
# 输出:foo_name
  1. __getitem__、__setitem__、__delitem__ 用于索引操作,如字典、列表。以上分别表示获取、设置、删除数据 字典
代码语言:javascript
复制
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__

列表

代码语言:javascript
复制
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__ 用于迭代器,之所以列表、字典、元组可以进行for循环,是因为类型内部定义了 iter
代码语言:javascript
复制
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]) ,所以相当于如下:

代码语言:javascript
复制
obj = iter([11,22,33,44])
for i in obj:
    print(i)

For循环迭代器输出

代码语言:javascript
复制
obj = iter([11,22,33,44])
while True:
    try:
        val = next(obj)
        print(val)
    except StopIteration as e:
        break

还有__metaclass__也是非常重要的一个,这个牵扯到反射机制,我们放在下一篇进行详细说明,今天的文章就到这里了,你有没有Get到新技能呢?欢迎留言一起探讨

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 python爬虫实战之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类成员的修饰符
    • 普通字段
      • 静态字段
      • 类的特殊成员
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档