Apakah model objek Python?

王林
王林ke hadapan
2023-05-18 21:02:431525semak imbas

Dalam teori berorientasikan objek, terdapat dua konsep teras: kelas dan kejadian. Kelas boleh dianggap sebagai templat, dan contoh ialah objek yang dibuat berdasarkan templat ini. Dalam Python, kedua-dua kelas dan contoh dianggap objek, iaitu objek kelas (atau objek jenis) dan objek contoh.

Untuk mengelakkan kekaburan berikutnya, kami membahagikan objek kepada tiga jenis di sini:

  • Objek kelas terbina dalam: seperti int, str, senarai, jenis, objek, dsb.;

  • Objek kelas tersuai: kelas yang ditakrifkan oleh kata kunci kelas Sudah tentu, kami juga akan merujuknya dan objek kelas terbina dalam di atas sebagai objek kelas (. atau taip objek);

  • Objek instance: tika yang dibuat oleh objek kelas (objek kelas terbina dalam atau objek kelas tersuai); >Terdapat dua hubungan berikut antara objek :

is-kind-of: sepadan dengan hubungan antara subkelas dan kelas induk dalam teori berorientasikan objek; 🎜>

is-instance- of: Sepadan dengan hubungan antara objek instance dan objek kelas dalam teori berorientasikan objek
  • Mari kita berikan contoh:
  • class Girl(object):
    
        def say(self):
            return "古明地觉"
    
    girl = Girl()
    print(girl.say())  # 古明地觉
    Kod ini mengandungi tiga jenis Objek di atas: objek (objek kelas terbina dalam), Girl (objek kelas tersuai), gadis (objek contoh).
  • Jelas sekali terdapat hubungan jenis antara Gadis dan objek, iaitu Gadis ialah subkelas objek. Perlu dinyatakan bahawa semua kelas dalam Python3 (kecuali objek) mewarisi objek secara lalai Walaupun kami tidak mewarisi objek secara eksplisit di sini, kami akan mewarisinya secara lalai, tetapi demi penjelasan, kami telah menulisnya.

  • Selain Gadis sebagai subkelas objek, kita juga dapat melihat bahawa terdapat satu-contoh-perhubungan antara gadis dan Gadis, iaitu, gadis ialah contoh Gadis. Sudah tentu, jika kita melangkah lebih jauh, terdapat juga contoh-hubungan antara gadis dan objek, dan gadis juga merupakan contoh objek.
class Girl(object):
    pass
    
girl = Girl()
print(issubclass(Girl, object))  # True 
print(type(girl))  # <class &#39;__main__.Girl&#39;>
print(isinstance(girl, Girl))  # True
print(isinstance(girl, object))  # True

Gadis mendapat contoh perempuan selepas dijadikan instantiated, jadi jenis panggilan(perempuan) akan mengembalikan objek kelas Gadis. Girl ialah objek contoh kelas Objek kerana ia mewarisi kelas Objek. Mengenai prinsip ini, kami akan memperkenalkannya perlahan-lahan.

Python juga menyediakan beberapa cara untuk mengesan perhubungan ini Selain daripada jenis di atas, anda juga boleh menggunakan atribut __class__ objek untuk mengesan perhubungan adalah-contoh antara objek dan objek lain yang wujud. .

Atribut __bases__ bagi objek boleh mengesan jenis-hubungan antara objek dan objek lain. Selain itu, Python juga menyediakan dua fungsi issubclass dan isinstance untuk mengesahkan sama ada hubungan yang kami jangkakan wujud antara dua objek.

class Girl(object):
    pass 

girl = Girl()
print(girl.__class__)  # <class &#39;__main__.Girl&#39;>
print(Girl.__class__)  # <class &#39;type&#39;>
# __class__是查看自己的类型是什么,也就是生成自己的类
# 而在介绍 Python 对象的时候,我们就看到了
# 任何一个对象都至少具备两个东西: 一个是引用计数、一个是类型
# 所以 __class__ 是所有对象都具备的

# __base__只显示直接继承的第一个类
print(Girl.__base__)  # <class &#39;object&#39;>
# __bases__ 会显示直接继承的所有类,以元组的形式
print(Girl.__bases__)  # (<class &#39;object&#39;>,)

Jom lukis gambar untuk diringkaskan:

Selain itu, anda perlu memberi perhatian kepada jenis dan objek di dalamnya:

Jenis dan objek mempunyai hubungan jenis-jenis, kerana jenis ialah subkelas objek; kerana objek ialah objek contoh jenis ;

Apakah model objek Python?

Sesetengah orang mungkin tertanya-tanya mengapa ini berlaku, dan mengenai perkara ini, saya membincangkannya secara terperinci dalam artikel Pertikaian antara jenis dan objek. Jika anda berminat, anda boleh klik untuk membacanya.

    Ringkasnya, struktur yang sepadan dengan taip di lapisan bawah ialah PyType_Type, dan struktur yang sepadan dengan objek di lapisan bawah ialah PyBaseObject_Type. Apabila mencipta objek, tetapkan ob_type dalaman kepada &PyType_Type apabila mencipta jenis, tetapkan tp_base dalaman kepada &PyBaseObject_Type.
  • Jadi kedua-dua takrifan itu bergantung antara satu sama lain, dan ia muncul pada masa yang sama, seperti yang akan kita lihat nanti.

  • Selain itu, jenis jenis ialah jenis itu sendiri, jadi:
  • Jenis objek contoh ialah objek jenis, dan jenis objek jenis ialah metaclass

Kelas asas semua jenis objek bertumpu kepada objek;

Oleh itu, Python boleh dianggap sebagai melaksanakan konsep segala-galanya sebagai objek yang melampau. Justru kerana inilah Python mempunyai ciri dinamik yang sangat baik.
  • Tetapi ia masih belum berakhir. Mari kita lihat gelagat objek kelas Girl Pertama sekali, ia menyokong tetapan atribut:

    class Girl(object):
        pass
    
    print(hasattr(Girl, "name"))  # False
    Girl.name = "古明地觉"
    print(hasattr(Girl, "name"))  # True
    print(Girl.name)  # 古明地觉

    Dalam bahasa statik lain, apabila kelas itu. ditakrifkan, atribut tidak boleh ditambah Tetapi dalam bahasa kita ia. Bagaimanakah Python melaksanakan penambahan atribut secara dinamik? Secara amnya, kami memikirkan kamus
  • sama seperti ruang nama global Kami meneka bahawa kelas juga harus mempunyai kamus atributnya sendiri Apabila menetapkan atribut dalam kelas, ia sama dengan menambah pasangan nilai kunci pada kamus. Begitu juga dengan operasi lain Juga serupa dengannya.

    class Girl(object):
        pass
    
    print(Girl.__dict__.get("name", "不存在"))  # 不存在
    Girl.name = "古明地觉"
    print(Girl.__dict__.get("name"))  # 古明地觉

    adalah serupa dengan mengendalikan pembolehubah global, tetapi ada satu perkara yang perlu diberi perhatian: kami tidak boleh menetapkan atribut secara langsung melalui kamus atribut kelas.
  • try:
        Girl.__dict__["name"] = "古明地觉"
    except Exception as e:
        print(e)  
    # &#39;mappingproxy&#39; object does not support item assignment

    虽然叫属性字典,但其实是 mappingproxy 对象,该对象本质上就是对字典进行了一层封装,在字典的基础上移除了增删改操作,也就是只保留了查询功能。要给类增加属性,可以使用直接赋值的方式或调用 setattr 函数。

    但在介绍如何篡改虚拟机的时候,我们提到过一个骚操作,可以通过 gc 模块拿到 mappingproxy 对象里的字典。

    import gc
    
    class Girl(object):
        pass
    
    gc.get_referents(Girl.__dict__)[0]["name"] = "古明地觉"
    print(Girl.name)  # 古明地觉

    并且这种做法除了适用于自定义类对象,还适用于内置类对象。但是工作中不要这么做,知道有这么个操作就行。

    除了设置属性之外,我们还可以设置函数。

    class Girl(object):
        pass
    
    Girl.info = lambda name: f"我是{name}"
    print(Girl.info("古明地觉"))  # 我是古明地觉
    
    # 如果实例调用的话,会和我们想象的不太一样
    # 因为实例调用的话会将函数包装成方法
    try:
        Girl().info("古明地觉")
    except TypeError as e:
        print(e) 
    """
    <lambda>() takes 1 positional argument but 2 were given
    """    
    
    # 实例在调用的时候会将自身也作为参数传进去
    # 所以第一个参数 name 实际上接收的是 Girl 的实例对象
    # 只不过第一个参数按照规范来讲应该叫做self
    # 但即便你起别的名字也是无所谓的
    print(Girl().info())  
    """
    我是<__main__.Girl object at 0x000001920BB88760>
    """

    所以我们可以有两种做法:

    # 将其包装成一个静态方法
    # 这样类和实例都可以调用
    Girl.info = staticmethod(lambda name: f"我是{name}")
    print(Girl.info("古明地觉"))  # 我是古明地觉
    print(Girl().info("古明地觉"))  # 我是古明地觉
    
    # 如果是给实例用的,那么带上一个 self 参数即可
    Girl.info = lambda self, name: f"我是{name}"
    print(Girl().info("古明地觉"))  # 我是古明地觉

    此外我们还可以通过 type 来动态地往类里面进行属性的增加、修改和删除。

    class Girl(object):
    
        def say(self):
            pass
    
    print(hasattr(Girl, "say"))  # True
    # delattr(Girl, "say") 与之等价
    type.__delattr__(Girl, "say")
    print(hasattr(Girl, "say"))  # False
    # 我们设置一个属性吧
    # 等价于 Girl.name = "古明地觉"
    setattr(Girl, "name", "古明地觉")
    print(Girl.name)  # 古明地觉

    事实上调用 getattr、setattr、delattr 等价于调用其类型对象的__getattr__、__setattr__、__delattr__。

    所以,一个对象支持哪些行为,取决于其类型对象定义了哪些操作。并且通过对象的类型对象,可以动态地给该对象进行属性的设置。Python 所有类型对象的类型对象都是 type,通过 type 我们便可以控制类的生成过程,即便类已经创建完毕了,也依旧可以进行属性设置。

    但是注意:type 可以操作的类只能是通过 class 定义的动态类,而像 int、list、dict 等静态类,它们是在源码中静态定义好的,只不过类型设置成了 type。一言以蔽之,type 虽然是所有类对象的类对象,但 type 只能对动态类进行属性上的修改,不能修改静态类。

    try:
        int.name = "古明地觉"
    except Exception as e:
        print(e)
    """
    can&#39;t set attributes of built-in/extension type &#39;int&#39;
    """
    
    try:
        setattr(int, "ping", "pong")
    except Exception as e:
        print(e)
    """
    can&#39;t set attributes of built-in/extension type &#39;int&#39;     
    """

    内置类和扩展类的属性无法被设置,这是由于内置类在解释器启动后就已被初始化。可以通过报错信息观察到这点。我们所说的扩展类,是指我们使用 Python/C API 编写的扩展模块中的类,其与内置类具有相同的地位。

    因此内置类和使用 class 定义的类本质上是一样的,都是 PyTypeObject 对象,它们的类型在 Python 里面都是 type。不同的是,内置类在底层是以静态初始化方式实现的,因此我们无法通过动态设置属性的方式来操作它们(除非使用 gc 模块)。

    但是为什么不可以对内置类和扩展类进行属性设置呢?首先我们要知道 Python 的动态特性是虚拟机赐予的,而虚拟机的工作就是将 PyCodeObject 对象翻译成 C 的代码进行执行,所以 Python 的动态特性就是在这一步发生的。

    同理,扩展类和内置类在解释器启动后都已经被静态初始化,并直接指向 C 一级的数据结构。它们与解释执行绕开了相应过程,因此无法在其上动态添加属性。

    不光内置的类本身,还有它的实例对象也是如此。

    a = 123
    print(hasattr(a, "__dict__"))  # False

    我们发现它甚至连自己的属性字典都没有,因为解释器对于内置类对象的实例对象,其内部的属性和方法是已知的。由于底层代码已被固定且不允许修改,因此在实现虚拟机时无需创建属性字典,以节省内存。

Atas ialah kandungan terperinci Apakah model objek Python?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam