前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 类与继承

Python 类与继承

作者头像
回天
发布2023-04-25 16:07:41
6910
发布2023-04-25 16:07:41
举报
文章被收录于专栏:Ga1@xy's W0r1dGa1@xy's W0r1d

「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__ 关系

在使用类名创建对象时,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 类本身

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

img
img
代码语言: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'>

类属性与方法

私有属性

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

代码语言: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__

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__

二者都是访问属性的方法,不同的是所有通过实例访问属性都会触发 __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__

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

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

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

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

*未完待续...

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-02-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 类与对象
    • self参数
      • __new__ 与 __init__ 关系
        • 继承关系
          • 实例化关系
            • type、object、class 之间关系
              • 类属性与方法
                • 私有属性
                • 私有方法
                • 静态方法
                • 类方法
                • 实例方法
            • 函数与魔法方法
              • super()
                • dir() 与 __dict__
                  • __getattr__ 与 __getattribute__
                    • type() 与 __class__
                      • __base__ 与 __bases__
                        • __mro__
                          • __subclasses__()
                            • __globals__ 与 func_globals
                              • 内建模块
                                • __getitem__
                                • *未完待续...
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档