>  기사  >  백엔드 개발  >  Python의 객체 모델은 무엇입니까?

Python의 객체 모델은 무엇입니까?

王林
王林앞으로
2023-05-18 21:02:431434검색

객체 지향 이론에는 클래스와 인스턴스라는 두 가지 핵심 개념이 있습니다. 클래스는 템플릿으로 간주할 수 있으며 인스턴스는 이 템플릿을 기반으로 생성된 개체입니다. Python에서는 클래스와 인스턴스 모두 객체, 즉 클래스 객체(또는 유형 객체)와 인스턴스 객체로 간주됩니다.

이후의 모호함을 피하기 위해 여기서는 객체를 세 가지 유형으로 나눕니다.

  • 내장 클래스 객체: int, str, list, type, object 등과 같은

  • 사용자 정의 클래스 객체: 클래스 키를 통해 단어로 정의된 클래스와 위의 내장 클래스 객체를 클래스 객체(또는 유형 객체)로 참조할 수도 있습니다.

  • 인스턴스 객체: 클래스 객체에 의해 생성된 인스턴스(내장- 클래스 객체 또는 사용자 정의 클래스 객체) ;

객체 간에는 다음 두 가지 관계가 존재합니다.

  • is-kind-of: 객체 지향 이론에서 하위 클래스와 상위 클래스 간의 관계에 해당합니다. is-instance-of: 객체지향 이론에서 인스턴스 객체와 클래스 객체 사이의 관계에 해당합니다.

  • 예를 들어보겠습니다.

    class Girl(object):
    
        def say(self):
            return "古明地觉"
    
    girl = Girl()
    print(girl.say())  # 古明地觉
  • 이 코드에는 위의 세 객체가 포함되어 있습니다. 객체(내장 클래스 객체) , Girl(사용자 정의 클래스 개체), girl(인스턴스 개체).

분명히 Girl과 객체 사이에는 일종의 관계가 있습니다. 즉, Girl은 객체의 하위 클래스입니다. Python3의 모든 클래스(객체 제외)는 기본적으로 객체를 상속한다는 점을 언급할 필요가 있습니다. 여기서 명시적으로 객체를 상속하지 않더라도 기본적으로 객체를 상속하지만 설명을 위해 작성하겠습니다.

Girl이 객체의 하위 클래스라는 것 외에도 girl과 Girl 사이에는 is-instance-of 관계가 있음을 알 수 있습니다. 즉, girl은 Girl의 인스턴스입니다. 물론 한걸음 더 나아가면 소녀와 사물 사이에도 is-instance-of 관계가 있고, 소녀 역시 사물의 인스턴스이다.

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

Girl은 인스턴스화된 후 girl 인스턴스를 가져오므로 type(girl)을 호출하면 Girl 클래스 개체가 반환됩니다. Girl은 Object 클래스를 상속받기 때문에 Object 클래스의 인스턴스 객체입니다. 이에 대한 원리는 천천히 소개하도록 하겠다.

Python은 위의 유형 외에도 객체의 __class__ 속성을 사용하여 객체와 다른 객체 간의 is-instance-of 관계를 감지할 수 있는 몇 가지 방법도 제공합니다.

그리고 객체의 __bases__ 속성을 통해 객체와 다른 객체 간의 is-kind 관계를 감지할 수 있습니다. 또한 Python은 두 객체 사이에 우리가 기대하는 관계가 존재하는지 확인하기 위해 issubclass와 isinstance라는 두 가지 함수도 제공합니다.

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;>,)

요약할 수 있는 그림을 그려 봅시다:

또한, 내부의 유형과 객체에 주의해야 합니다:

Python의 객체 모델은 무엇입니까?

유형과 객체는 is-kind-of 관계를 가집니다.

  • 객체와 유형은 is-instance-of 관계를 가집니다. 왜냐하면 object는 유형의 인스턴스 객체이기 때문입니다.

  • 어떤 사람들은 이것이 왜 그런지 궁금해할 수도 있습니다. 유형과 객체 사이의 분쟁에 대해 이 기사를 썼습니다. 관심이 있으시면 클릭하여 읽어보실 수 있습니다.

  • 간단히 말하면, 맨 아래 레이어의 유형에 해당하는 구조체는 PyType_Type이고, 맨 아래 레이어의 객체에 해당하는 구조체는 PyBaseObject_Type입니다. 객체를 생성할 때 내부 ob_type을 &PyType_Type으로 설정하고, 유형을 생성할 때 내부 tp_base를 &PyBaseObject_Type으로 설정합니다.

그러므로 두 정의는 서로 의존적이며 동시에 나타납니다. 나중에 살펴보겠지만요.

또한 유형의 유형은 유형 자체이므로 다음과 같습니다.

인스턴스 객체의 유형은 객체 유형이고 유형 객체의 유형은 메타클래스입니다.

  • 모든 유형 객체의 기본 클래스는 다음으로 수렴됩니다. object;

  • 모든 객체의 유형은 유형으로 수렴됩니다.

따라서 Python은 모든 것이 객체라는 개념을 극단적으로 구현한다고 볼 수 있습니다. 이러한 뛰어난 동적 특성.

Python의 객체 모델은 무엇입니까?아직 끝나지 않았습니다. Girl 클래스 객체의 동작을 살펴보겠습니다. 우선 속성 설정을 지원합니다.

class Girl(object):
    pass

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

다른 정적 언어에서는 클래스가 정의되면 속성을 추가할 수 없지만 우리 언어는 가능해요. Python은 동적으로 속성 추가를 어떻게 구현합니까? 일반적으로 우리는 사전을 생각합니다

전역 네임스페이스와 마찬가지로 클래스에도 자체 속성 사전이 있어야 한다고 생각합니다. 클래스에서 속성을 설정하는 것은 사전에 키-값 쌍을 추가하는 것과 같습니다. 비슷하다.

class Girl(object):
    pass

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

는 전역 변수를 조작하는 것과 유사하지만 한 가지 주의할 점은 클래스의 속성 사전을 통해 직접 속성을 설정할 수 없다는 것입니다.

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

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

위 내용은 Python의 객체 모델은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제