python-面向对象编程-成员/嵌套/特殊成员

1、成员

(1)变量

  • 实例变量(字段/属性)

    • 对象实例化后,创建的对象有一个类指针指向内存中的类,因此对象才可以调用类的方法

    • 实例变量是对象所私有的

    • 实例变量分为公有和私有

      • 公有实例变量,即默认的实例变量,在类的内部和外部均可访问

      • 私有实例变量,使用字段修饰符的实例变量,在类的外部不可访问

        class Foo:
            def __init__(self, name, age):
                self.name = name  # 公有实例变量
                self.__age = age  # 私有实例变量
                            
        # 在外部可以通过下面这种方法访问私有实例变量
        obj = Foo("xzj", 123)
        print(obj._Foo_name)  # 在外部访问私有实例变量的方法
        
  • 类变量(静态字段)

    • 在类的定义中创建的变量(普通变量或者函数变量)为类变量

    • 类变量是该类所共享的,对象既可以访问实例变量,也可以访问类变量(先查找实例变量),但是类不可以访问实例变量

      class Foo:
          num = 1  # num为类变量,是该类的共享变量,在类的内外部均可访问
          def __init__():
              pass
      
    • 类变量也分为公有和私有

      • 公有类变量,同公有实例变量类似

      • 私有类变量,同私有实例变量类似,例子如下

        class Foo:
            name = "xzj"  # 公有类变量
            __age = 21  # 私有类变量
                        
        # 在外部访问私有类变量的方法同访问私有实例变量一样
        
  • 私有变量的继承问题

    • 派生类可以访问父类的公有变量,但不能访问其私有变量
    class Base(Object):
        name = "xzj"
        __age = 21
        def f1(self):
            print(self.__age)
    
    class Son(Base):
        def f2(self):
            print(self._Base__age)  # 在派生类内部也不可以直接访问父类的私有变量
    
    obj = Son()
    obj.f1()  # 这是可以执行的,因为f1函数是在Base内部定义的
    

(2)方法

  • 实例方法:即普通的定义

  • 静态方法

    • 若方法中没有调用到实例变量,则可以写成静态方法,即该方法与具体对象无关,是该类的公有方法
    class Foo(Object):
        @staticmethod  # 定义静态方法要加上这个
        def display(name):
            print(name)
            
    Foo.display()  # 静态方法可以直接通过类来调用
    Foo().display()  # 也可以通过对象调用   
    
  • 类方法

2、嵌套

  • 对象和对象之间是可以嵌套使用的
In [3]: class Stu(object):
   ...:     def __init__(self, name, age):
   ...:         self.name = name
   ...:         self.age = age
   ...:         self.school = None         

In [4]: class School(object):
   ...:     def __init__(self, name):
   ...:         self.name = name
   ...:         

In [5]: xx1 = Stu('abc', 20)

In [7]: yy1 = School('university')

In [8]: xx1.school = yy1

In [11]: xx1.school.name
Out[11]: 'university'

3、主动调用其它类的方法

  • 调用某个类的方法时可以直接使用类名.方法(对象)的方式调用
  • 这种用法在一个类中调用其它类的方法时有用
In [12]: class Obj1(object):
    ...:     def __init__(self, name):
    ...:         self.name = name
    ...:     def f1(self):
    ...:         print('f1: ', self.name)
    ...:             

In [13]: xxx = Obj1('aaa')

In [14]: xxx.f1()
f1:  aaa

In [15]: Obj1.f1(xxx)
f1:  aaa

4、特殊成员

(1)__init__()方法

In [3]: class Foo(object):
   ...:     def __init__(self, name):
   ...:         self.name = name
   ...:         

In [4]: obj = Foo('abc')
  • 当实例化对象时自动执行__init__()方法

(2)__call__()方法

In [5]: class Foo(object):
   ...:     def __init__(self, name):
   ...:         self.name = name
   ...:     def __call__(self, *args, **kwargs):
   ...:         print('This is __call__()')
   ...:         print(args)
   ...:         print(kwargs)
   ...:             

In [6]: obj = Foo('abc')

In [7]: obj()
This is __call__()
()
{}

In [8]: obj(1,2,3,4, k1='aaa', k2='bbb')
This is __call__()
(1, 2, 3, 4)
{'k1': 'aaa', 'k2': 'bbb'}
  • __call__()方法在直接使用对象名作为函数时自动调用

(3)__getitem__()方法

  • 该方法和列表的索引很像

    In [13]: l = [1,2,3,4]
    
    In [14]: l[0]
    Out[14]: 1
    
    In [16]: l.__getitem__(0)
    Out[16]: 1
    
    • 可以看到列表的索引实际上是调用了__getitem__()方法来实现的,所以其它的对象也是有相似语法的
  • 例子如下:

    In [9]: class Foo(object):
       ...:     def __init__(self, name):
       ...:         self.name = name
       ...:     def __getitem__(self, item):
       ...:         print('The item is: ', item)
       ...:             
    
    In [10]: obj = Foo('abc')
    
    In [11]: obj[123]
    The item is:  123
    
    In [12]: obj['hhh']
    The item is:  hhh
    
    • 当使用如上格式时,会自动调用__getitem__()方法

(4)__setitem__()方法

  • 该方法和字典的赋值类似

    In [17]: dic = {1:100, 2:200}
    
    In [18]: dic[3] = 300
    
    In [19]: dic
    Out[19]: {1: 100, 2: 200, 3: 300}
    
    In [20]: dic.__setitem__(4, 400)
    
    In [21]: dic
    Out[21]: {1: 100, 2: 200, 3: 300, 4: 400}
    
    • 当我们通过上面的语法给字典赋值时,实际上是调用了__setitem__()方法的,同样其它对象也是有类似语法的
  • 例子如下

    In [22]: class Foo(object):
        ...:     def __init__(self, name):
        ...:         self.name = name
        ...:     def __setitem__(self, key, value):
        ...:         print('The key is: ', key)
        ...:         print('The value is: ', value)
        ...:                    
    
    In [23]: obj = Foo('aaa')
    
    In [24]: obj[1] = 100
    The key is:  1
    The value is:  100
    
    • 使用如上语法时,会自动调用对象的__setitem__()方法

(5)with obj_name as val_name语法

  • 在文件操作中有一种with file_obj as f的语法,实际上是执行了__enter__()方法和__exit__()方法

  • 这种语法对其它对象也是成立的,例子如下:

    In [67]: class Foo(object):
        ...:     def __enter__(self):
        ...:         print('This is __enter__!!')
        ...:         return [1,2,3]
        ...:     def __exit__(self, ex_type, ex_val, tb):
        ...:         print(ex_type)
        ...:         print(ex_val)
        ...:         print(tb)
        ...:         print('This is __exit__!!!')
        ...:         
        ...:     
    
    In [68]: obj = Foo()
    
    In [69]: with obj as ret:
        ...:     print('----\n', ret, '\n----')
        ...:     
    This is __enter__!!
    ----
     [1, 2, 3] 
    ----
    None
    None
    None
    This is __exit__!!!
    
    • with ··· as ···是上下文管理器的语法

    • with obj触发__enter__()方法,传递的参数就是obj这个对象

    • __enter__()的返回值会赋给as ret中的ret

    • 当发生异常时会触发__exit__()方法

      • 需要注意的是__exit__()方法的四个参数似乎都必须使用(这里没太懂,如果有知道的,望在评论区告知,万分感谢),一个错误例子如下
      In [53]: class Foo(object):
          ...:     def __enter__(self):
          ...:         print('This is __enter__!!')
          ...:         return [1,2,3]
          ...:     def __exit__(self, exc_type, exc_val, exc_tb):
          ...:         print('This is __exit__!!!')
          ...:         
      
      In [54]: with obj as ret:
          ...:     print('------')
          ...:     print(ret)
          ...:     print('------')
          ...:     
      This is __enter__!!
      ------
      [1, 2, 3]
      ------
      ---------------------------------------------------------------------------
      TypeError                                 Traceback (most recent call last)
      <ipython-input-54-e6c137c400ba> in <module>()
            2     print('------')
            3     print(ret)
      ----> 4     print('------')
            5 
      
      TypeError: __exit__() takes 1 positional argument but 4 were given
      

(6)构造方法__new__()

  • 创建对象(对象实例化)时,实际上是先执行了object(所有对象都是继承的object)的__new__()方法,这个方法会创建一个空的object对象,然后再执行__init__()方法,返回一个Foo对象(假设我们创建的对象是Foo类的)

  • 一个例子如下:

    In [70]: class Foo(object):
        ...:     def __init__(self, name):
        ...:         print('This is in __init__')
        ...:         self.name = name
        ...:     def __new__(cls, *args, **kwargs):
        ...:         print('This is in __new__')
        ...:         return object.__new__(cls)
        ...:     
    
    In [71]: obj = Foo('aaa')
    This is in __new__
    This is in __init__
    
    • return object.__new__(cls)中的参数cls是指Foo这个类,__new__()方法会创建一个空对象

    • __new__()方法返回的值是赋给__init__(self, name)中的self

    • __init__(self, name)方法实际上是初始化对象,而不是构造对象

    • 下例可以帮助更好理解

      In [74]: class Foo(object):
          ...:     def __init__(self, name):
          ...:         print('This is in __init__')
          ...:         self.name = name
          ...:     def __new__(cls, *args, **kwargs):
          ...:         print('This is in __new__')
          ...:         emptyObj = object.__new__(cls)
          ...:         print('emptyObj\'s address is: ', id(emptyObj))
          ...:         return emptyObj
          ...:     
      
      In [75]: obj = Foo('aaa')
      This is in __new__
      emptyObj's address is:  4580299944
      This is in __init__
      
      In [76]: print(id(obj))
      4580299944
      
      • 可以看到创建的空对象和初始化后的对象内存地址是相同的

(7)其它的

  • 这些特殊方法还有很多,实际上都是当语句满足某种语法时就会执行对象的特定的方法

  • 比如说字符串可以相加,str1 + str2实际上是执行了__add__()方法

  • 以列表的特殊方法作为例子看一下

    class list(object)
     |  list() -> new empty list
     |  list(iterable) -> new list initialized from iterable's items
     |  
     |  Methods defined here:
     |  
     |  __add__(self, value, /)
    ...
     |  __contains__(self, key, /)
    ...
     |  __delitem__(self, key, /)
    ... 
     |  __eq__(self, value, /)
    ... 
     |  __ge__(self, value, /)
    ... 
     |  __getattribute__(self, name, /)
    ... 
     |  __getitem__(...)
    ... 
     |  __gt__(self, value, /)
    ...
     |  __iadd__(self, value, /)
    ...  
     |  __imul__(self, value, /)
     ... 
     |  __init__(self, /, *args, **kwargs)
    ...  
     |  __iter__(self, /)
    ...  
     |  __le__(self, value, /)
    ...  
     |  __len__(self, /)
    ...  
     |  __lt__(self, value, /)
    ...  
     |  __mul__(self, value, /)
    ...  
     |  __ne__(self, value, /)
    ...  
     |  __new__(*args, **kwargs) from builtins.type
    ... 
     |  __repr__(self, /)
    ... 
     |  __reversed__(...)
    ...  
     |  __rmul__(self, value, /)
    ...  
     |  __setitem__(self, key, value, /)
    ...  
     |  __sizeof__(...)
    ... 
     |  append(...)
     |      L.append(object) -> None -- append object to end
    ... # 后面的略
    
  • 比如说使用+ - * / %这些数学符号会触发特殊方法,在自己编写数学相关的类时非常有用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值