Python 类与继承

2023-04-25 16:07:41 浏览数 (1)

「python中一切皆对象」

类与对象

self参数

self参数用于对当前类中实例的引用,必须作为该类中任何函数的第一个参数,但可以不必命名为 self

代码语言:javascript复制
class A:
    def add(self, x, y):
        self.x = x   1
        self.y = y   1

class B:
    def add(abc, x, y):
        abc.x = x   1
        abc.y = y   1

__new__ 与 __init__ 关系

  • https://xxhs-blog.readthedocs.io/zh_CN/latest/how_to_be_a_rich_man.html
  • https://www.cnblogs.com/wdliu/p/6757511.html
  • https://blog.csdn.net/fjswcjswzy/article/details/105637086

在使用类名创建对象时,Python 解释器会首先调用 __new__ 方法为对象分配空间,并返回对象的引用,Python 解释器在获得对象的引用后,将引用作为第一个参数,传递给 __init__

  • __new__ 通常用于控制生成一个类实例的过程,它是类级别的方法
  • __init__通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性, 做一些额外的操作,发生在类实例被创建完以后,它是实例级别的方法

继承关系

子类继承于父类,子类拥有其自身及父类的方法和属性,同名的子类方法和属性将会覆盖父类的方法和属性

代码语言:javascript复制
class Parent:
    def a(self):
        self.title = "In parent"
        print(self.title)

class Son(Parent):
    def b(self):
        print(self.title)

c = Son()
c.a()
# In parent
c.b()
# In parent
c.title
# In parent

可以通过 __bases__ 方法查看一个类的(所有)父类

代码语言:javascript复制
Son.__bases__
# (<class '__main__.Parent'>,)

实例化关系

实例化关系是一个从抽象到具体的关系,实例是类的具体表现形式

代码语言:javascript复制
class A:
    pass

a = A()    # a 就是类 A 的实例化对象

如果想要查看一个对象是由哪个类实例化而来,可以通过 type() 命令或者 __class__ 方法查看

代码语言:javascript复制
type(a)
# <class '__main__.A'>
a.__class__
# <class '__main__.A'>

type、object、class 之间关系

  • https://zhuanlan.zhihu.com/p/100885824
  • object 类是所有类(class)的父类,包括 type 类,object 类的父类为空
  • type 类是所有类的类型,即所有类都由 type 类的实例化而来,包括 type 类本身

下图中红色箭头指向其父类,蓝色箭头指向其类型(由哪个类实例化而来)

代码语言:javascript复制
class A:
    pass

a = A()
A.__bases__ # (<class 'object'>,)
object.__bases__ # ()
type.__bases__ # (<class 'object'>,)
type(a) # <class '__main__.A'>
type(A) # <class 'type'>
type(type) # <class 'type'>
type(object) # <class 'type'>

类属性与方法

  • https://www.runoob.com/python/python-object.html
  • https://www.cnblogs.com/chenhuabin/p/10055316.html
私有属性

以两个下划线开头,声明为私有属性,则类外部无法对其进行调用

代码语言:javascript复制
class A:
    __secret = 0
    def B(self):
        self.__secret  = 1

a = A()
a.__secret

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute '__secret'

虽然无法直接调用私有属性数据,但是可以通过 object._className__attrName (对象名._类名__私有属性名)进行访问

代码语言:javascript复制
class A:
    __secret = 123

a = A()
a._A__secret
# 123
私有方法

同样是以两个下划线开头,声明为私有方法,类外部无法对其进行调用

代码语言:javascript复制
class A:
    secret = 0
    def __B(self):
        self.secret  = 1
        print(self.secret)

a = A()
a.B()

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'B'
静态方法

在定义时,使用 @staticmethod 装饰器来进行修饰,无须传入 self 等参数即可创建方法。在调用过程中,无需将类实例化,可以直接通过 类名.方法名 进行调用,也可以在实例化后通过 实例名.方法名 进行调用,在静态方法内部,只能通过 类名.类变量名 方式访问变量

代码语言:javascript复制
class A:
    a = 1
    @staticmethod
    def add():
        print(A.a   1)

aa = A()
aa.a
# 1
aa.add()
# 2
A.a
# 1
类方法

在定义时,使用 @classmethod 装饰器进行修饰,同时需要指定传入第一个参数为 cls(命名可以自定义),在调用过程中,可以直接通过 类名.方法名 进行调用,也可以在实例化后通过 实例名.方法名 进行调用,在方法内部,可以通过 类名.类变量名 方式访问变量,也可以通过 cls.类变量名 方式访问

代码语言:javascript复制
class A:
    a = 1
    @classmethod
    def add(cls):
        print(A.a   1)
        print(cls.a   2)

aa = A()
aa.a
# 1
aa.add()
# 2
# 3
A.a
# 1
实例方法

实例方法不需要修饰器进行修饰,但是要求指定传入第一个参数为 self(命名可以自定义)。实例方法可以访问实例变量,类方法与静态方法则不能。实例方法内部只能通过 类名.类变量名 方式访问变量,在调用时可以通过 实例名.实例方法名 方式调用,如果想要通过 类名.实例方法名 方式调用,必须显式传入实例名

代码语言:javascript复制
class A:
    a = 1
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def test(self):
        print(A.a)
        print(self.name, self.age) # 访问实例变量

aa = A("abc", 18)
aa.test()
# 1
# abc 18
A.test(aa) # 显式传入实例名作为参数
# 1
# abc 18

函数与魔法方法

super()

用来调用父类(超类)的方法,若父类和超类中有同名方法,优先调用父类

Python2 用法:super(父类/超类名, self).函数名

Python3 用法:super().函数名

父类、子类、超类的关系:

  • Son直接继承Parent,二者之间叫做子类和父类
  • Parent直接继承Grandparent,二者之间叫做子类和父类
  • Son间接继承Grandparent,Grandparent是Son的超类
代码语言:javascript复制
class Grandparent:
    def xor(self, x, y):
        res = x ^ y
        print(res)
        print("In Grandparent")

class Parent(Grandparent):
    def add(self, x, y):
        sum = x   y
        print(sum)
        print("In Parent")

class Son(Parent):
    def add(self, x, y):
        super().add(x, y)
    def xor(self, x, y):
        super().xor(x, y)

a = Son()
a.add(1, 2)
# 3
# In Parent
a.xor(3, 3)
# 0
# In Grandparent

dir() 与 __dict__

  • https://cloud.tencent.com/developer/article/1581164
  • http://c.biancheng.net/view/2374.html

dir() 函数返回一个列表,列表的内容是该对象的所有属性,包括从父类继承的属性

代码语言:javascript复制
class A:
    name = 'abc'
    def aa(self):
        pass

class B(A):
    def bb(self):
        print(self.name)

dir(A)
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'aa', 'name']
dir(B)
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'aa', 'bb', 'name']

_dict_ 返回一个字典,字典内容是当前对象的属性(不包括父类),属性名作为键,属性值作为键对应的值

代码语言:javascript复制
A.__dict__
# mappingproxy({'__module__': '__main__', 'name': 'abc', 'aa': <function A.aa at 0x7f2aed150a60>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})
B.__dict__
# mappingproxy({'__module__': '__main__', 'bb': <function B.bb at 0x7f2aed150af0>, '__doc__': None})

实例的 __dict__ 属性只包含当前实例的实例属性,并不是所有有效属性,

代码语言:javascript复制
class A:
    def __init__(self):
        self.name = 'abc'
        self.age = 18

class B(A):
    def __init__(self):
        self.name = 'def'
        self.age = 20

a = A()
b = B()
a.__dict__
# {'name': 'abc', 'age': 18}
b.__dict__
# {'name': 'def', 'age': 20}

从这两个方法的对比,我们也可以看出来,__dict__ 得到的内容只是 dir() 的子集,dir() 中包含 __dict__ 中的属性

__getattr__ 与 __getattribute__

  • https://www.cnblogs.com/huchong/p/9306459.html
  • https://juejin.cn/events/all

二者都是访问属性的方法,不同的是所有通过实例访问属性都会触发 __getattribute__ 方法,而当访问的属性不存在时,会继续触发 __getattr__,也就是说 __getattribute__ 一定会在 __getattr__ 之前触发,当 __getattribute__ 有返回值时就不会再触发 __getattr__

  • __getattr__(self, name)
    • self:函数中固定第一个参数
    • name:参数名
    • 该方法可以自定义返回值,若不定义,则在方法执行结束后触发 AttributeError
  • __getattribute__(self, name)
    • self:函数中固定第一个参数
    • name:参数名
代码语言:javascript复制
class A:
    test = 123
    def __getattr__(self, name):
        print("__getattr__ is called")
        return name   " not found"
    def __getattribute__(self, name):
        print("__getattribute__ is called")
        return object.__getattribute__(self, name)

a = A()
a.test
# __getattribute__ is called
# 123
a.test2
# __getattribute__ is called
# __getattr__ is called
# 'test2 not found'
  • getattr(object, name[ ,default])
    • object:对象
    • name:参数名
    • dufault:默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError
  • object.__getattribute__(name)
    • object:对象
    • name:参数名
代码语言:javascript复制
class A:
    test = 1

a = A()
getattr(a, 'test')
# 1
a.__getattribute__('test')
# 1
getattr(a, 'test2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'test2'

这两个方法也可以用来命令执行

  • getattr(__import__('os'), 'system')('ls')
  • __import__('os').__getattribute__('system')('ls')

type() 与 __class__

两种方法都可以用来查看该实例是由哪个类实例化而来,也可以称为其所属类型

代码语言:javascript复制
class A:
    pass

a = A()    # a 就是类 A 的实例化对象
type(a)
# <class '__main__.A'>
a.__class__
# <class '__main__.A'>

().__class__
# <class 'tuple'>
[].__class__
# <class 'list'>
type(1)
# <class 'int'>

__base__ 与 __bases__

__base__ 可用于查看一个类的一个父类,符合菱形继承机制

代码语言:javascript复制
class A:
    pass

class B:
    pass

class C(B, A):
    pass

C.__base__
# <class '__main__.B'>

__bases__ 可用于查看一个类的全部父类, 但只能向上寻找一层

代码语言:javascript复制
class A:
    pass

class B(A):
    pass

class C(B, A):
    pass

C.__bases__
# (<class '__main__.B'>, <class '__main__.A'>)

__mro__

有关具体继承机制可参考:

  • https://hanjianwei.com/2013/07/25/python-mro/

简单来讲就是菱形继承机制,左侧优先,重复类保留最后一个

该方法用于查看类的调用顺序(继承关系)

代码语言:javascript复制
class A:
    pass

class B(A):
    pass

class C(B, A):
    pass

C.__mro__
# (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

__subclasses__()

获取一个类的所有子类,返回一个由所有子类构成的列表

这个方法只适用于新式类,新式类继承自 object,Python3版本中只支持新式类,Python2版本中可能不支持

代码语言:javascript复制
class A:
    pass

class B(A):
    pass

class C(B, A):
    pass

A.__subclasses__()
# [<class '__main__.B'>, <class '__main__.C'>]

type 类也具有此方法,但不同的是,type 类作为所有类别的实例化来源,它的 __subclasses__() 方法需要一个类别作为参数,而其他类则不需要

代码语言:javascript复制
A.__mro__[-1].__class__
# <class 'type'>

A.__mro__[-1].__class__.__subclasses__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor '__subclasses__' of 'type' object needs an argument

A.__mro__[-1].__class__.__subclasses__(A.__mro__[-1]) # 以 object 类作为参数为例
[<class 'type'>, <class 'weakref'>, <class 'weakcallableproxy'>, <class 'weakproxy'>, <class 'int'>, <class 'bytearray'>, <class 'bytes'>, <class 'list'>, <class 'NoneType'>, <class 'NotImplementedType'>, <class 'traceback'>, <class 'super'>, <class 'range'>, <class 'dict'>, <class 'dict_keys'>, <class 'dict_values'>, <class 'dict_items'>, <class 'dict_reversekeyiterator'>, <class 'dict_reversevalueiterator'>, <class 'dict_reverseitemiterator'>, <class 'odict_iterator'>, <class 'set'>, <class 'str'>, <class 'slice'>, <class 'staticmethod'>,......]

__globals__ 与 func_globals

__globals__ 可用于python2/3,以字典的形式返回函数所在的全局命名空间所定义的全局变量,即找到该函数所有能够调用内容

代码语言:javascript复制
class A:
    def test(self):
        pass

A.test.__globals__
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'A': <class '__main__.A'>}

func_globals 只用于python2,用途与 __globals__ 相同

代码语言:javascript复制
A.test.func_globals
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', 'A': <class __main__.A at 0x0000000003101768>, '__doc__': None, '__package__': None}

内建模块

在 python 中有许多不需要引用就能直接使用的函数,例如 openstrchr 等等,这些函数都包含在内建模块中,在 python2/3 中对于内建模块,有不同的表示方法

  • python2

在 python2 中,内建模块用 __builtin__ 表示,需要先引入才能查看

代码语言:javascript复制
import __builtin__
__builtin__

<module '__builtin__' (built-in)>

可以用 dir() 或者 __dict__ 方法来查看其中包含的各种函数

代码语言:javascript复制
dir(__builtin__)
__builtin__.__dict__

调用方法也很简单

代码语言:javascript复制
__builtin__.str(1)
  • python3

在 python3 中,内建模块用 builtins 表示,同样也要先引入才能查看,各种查看方法与调用方法与 python2 相同,不再赘述

  • 通用

在 python2/3 中,都有一个 __builtins__,它是 __builtin__builtins 的引用,它的好处是直接就可以使用,不需要事先 import

代码语言:javascript复制
dir(__builtins__)
__builtins__.str(1)

__getitem__

处理对象为序列,可以通过下标或键值方式返回序列中的值

代码语言:javascript复制
# 字符串
'abc'.__getitem__(0)
'a'

# 元组
(1, 2).__getitem__(0)


# 列表
[1, 2, 3].__getitem__(0)
1

# 字典
{'1':'a', '2':'b'}.__getitem__('1')
'a'

*未完待续...

可能也许大概应该好像似乎不一定会持续更新(

0 人点赞