Loading

Python 中 Type 和 Object

写这篇博文时十分忐忑~ 且谈一下我的认识,有错的欢迎留言指正。

明确几点

Python中一切皆对象
所有的类 都继承自 object,也就是说 object 是所有类的基类(超类)
type 也继承自 object 源码中type 的定义:class type(object):
type objcet 的类型 同时 object type 的超类
明确继承具有传递性。 继承了 家禽 家禽 又继承了 禽类 ,因此鸡也属于禽类,就是这样的关系。
类和实例关系。类也是对象,在两个对象中,一个是另一个的实例。例如鸡是一个类 叫小红的鸡是鸡的实例。
在Python3中,类和类型已经是一种东西了
在Python3 中只有类和非类两种对象。类型是"<class 'type'>" 那么它是类否则它是非类。听起来很绕,英文原文:There are only two kinds of objects in Python: to be unambiguous let's call these types and non-types. Non-types could be called instances, but that term could also refer to a type, since a type is always an instance of another type. Types could also be called classes, and I do call them classes from time to time.

class Chicken(object):    # 鸡类 他不仅仅是类 也是一个对象,它是 type 的对象
   pass

xiaohong = Chicken()   # 它是鸡类 这个类的对象

Python一切皆对象

1 在Python中 我们常说的 也是 对象 ,而它们的类是 元类 : type 简单点易于理解称为 类的类

2 内建的类(类型),object、list、tuple、dict。

3 自定义的类 MyClass 等等任何你喜欢的名字。

4 由 自定义的类 内建的类 实例化的对象就是我们常说的 对象 了,也叫对象的实例。

两种关系

在面向对象的世界里有两种关系,这与语言本身没有关系,只要是面向对象的语言都拥有这样的两种属性。类型实例关系和继承关系。

类型实例关系

即该类由谁创建

str('abc')  类 str 创建了实例 abc。

class Person(object):
    """ A simple Person class. """
monkey = Person()
# monkey  和  Person 就是 类型和实例的关系 type(monkey) --> <class '__main__.Person'>

继承关系

即类继承自谁

class MyList(list):
    """ A custom list class , it's a subclass of `list`. """
# MyList 就是 list 的子类, 它和list 就是 继承关系

type 和 object的关系

# 这是Python 源码中对type 的定义
class type(object):
    """
    type(object_or_name, bases, dict)
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    """
    ...
    
# 由此 可见 type 是 object 的子类 也就是 继承关系  <1> type 继承自 object

>>> type(object)
<class 'type'>
>>> type(type)
<class 'type'>

# 上面两行代码的输出很清晰  type 和 object 以及 type 与 type 存在 类型实例关系  
# <2> type 是 object 的类型
# <3> type 是 type 的类型

一个类/对象的诞生过程

类和对象的创建十分相似,类 本身也是对象~,他们由元类创建。

创建类的方法

class 关键字

关于这种方式无需赘述,如果下面的代码看不懂,那也没必要继续向下阅读了。

class MyClass(object):
    """This is a simple class for display how to create a class. """
    name = 'NewClass'

    def func(self):
        print('Hello class, my name is %s' % self.name)


new_obj = MyClass()
print(new_obj.func)

# <bound method MyClass.func of <__main__.MyClass object at 0x103b3b2e8>>
# Hello class, my name is NewClass

type函数

type 这个函数有点傻屌,它依据参数的不同拥有两种完全不同的行为,在程序设计中是很另类的,似乎也不符合Python的设计哲学。按照Python的 Style 他应该是两个函数才对,但是这是为了更好的向后兼容。

type 接受三个参数 第一个参数为类的名字, 第二个参数为继承列表, 第三个参数为属性字典(属性和方法)

def func(self):
    """ A simple function to bound NewClass. """
    print('Hello type, my name is %s' % self.name)

TypeClass = type('TypeClass', (object,), {'name': 'NewClass', 'func': func})

type_obj = TypeClass()
print(type_obj.func)
type_obj.func()

# <bound method func of <__main__.NewClass object at 0x100d2f128>>
# Hello type, my name is TypeClass

看看创建的细节

到这里我认为你已经对元类 type 有了一定的了解:

type就是Python在背后用来创建所有类的元类。包括字典 元组 字符串 类 甚至函数等等 都是由type创建。甚至你查看object 的类型也会告诉你他是 type 但是 type又继承自object 源码中写的很清晰。实质上他们都由虚拟机创建,同时objecttype属性是其子类type而type又继承object 不要问先有鸡还是先有蛋~ 你可以抽相处一个类型对象的概念,这个东西 类型是type 父类是object 当然仅仅是概念。不要在这里纠结~ 好吧~ !

类的创建会调用两个方法, __new__ 和 __init____new__ 用来从类实例化 对象 __init__ 负责实例化这个对象。

举个不恰当的例子,__new__ 方法就相当于 生孩子,__init__ 方法就相当于给这个孩子起名,上户口这样子。没有 __new__ 就没法__new__ 因此 __new__ 方法一定在 __init__ 之前 而__init__ 需要 __new__ 方法的结果 就是被实例化的对象(就生的那个孩子 没孩子 一切白搭) !

__call__ 是个神奇的方法, 它允许用户像使用函数那样使用类或对象,也就是使用() 就回来调用__all__

_call_

对象通过提供 __call(slef, *args ,**kwargs)__方法可以模拟函数的行为。即允许以类/对象() 的形式来调用。

元类的__call__方法在继承的类进行实例化时调用(此时实例化出的是个类,而不是对象)

_new_

将自身实例化时,也就是创建对象时调用的方法,该方法是一个静态方法,第一个参数为类本身,返回值为为该对象分配的空间。也就是被创建的这个对象。__new__方法至少需要一个cls 参数,表示的是将要被实例化的类本身,需要注意的是该方法必须返回一个空间,也就是返回实例化后的对象 return super().\__new__() 或者 object.__new__()

_init_

拿到__new__方法返回的对象,对这个新创建出来的对象进行一些初始化操作。

优雅的控制类和对象

使用上述的一些方法我们可以优雅的控制类的创建和使用。例如 我们可以自定义元类来设计优雅的API, 虽然元类的设计麻烦,但是使用其创建的类 使用起来将会非常舒服~ 例如Django 中的model的使用~ 真香~!

改写类来控制创建的对象

实现一个配置类,该类需要保证全局只有一个实例,并且需要保证同一时间 配置是一致的。也就是需要保证线程安全。下面是代码,通过控制 __new__ 方法来实现单例。

import threading

def synchronized(bar):
    """ threading lock for Config"""
    
    bar.__lock__ = threading.Lock()   # get lock
    
    def lock_func(*args, **kwargs):
        with bar.__lock__:
            return bar(*args, **kwargs)
    return lock_func


class Config(object):
    """ Singleton `Config` ."""
    _instance = None

    @synchronized
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

改写元类来控制创建的类

元类所创建的类会拥有元类拥有的方法。

通常的自定义的元类会重写 __new____call____init__。此时他们的第一个参数不再是对象,而是将要创建的类。__new__ 返回的也将是类, 而 __init__ 是添加类属性而不是实例属性。

假设上一步的操作中需要将配置类中所有的属性都变为大写的。以 _ 开头的属性除外,要怎么做呢?

将函数作为元类

假设上一步的操作中需要将配置类中所有的属性都变为大写的。以 _ 开头的属性除外,要怎么做呢?

import threading


def synchronized(bar):
    """ threading lock for Config"""

    bar.__lock__ = threading.Lock()  # get lock

    def lock_func(*args, **kwargs):
        with bar.__lock__:
            return bar(*args, **kwargs)

    return lock_func

def filter_config(cls_name, cls_mro, cls_attr):
    cls_attr = {k.upper(): v for k, v in cls_attr.items() if not k.startswith('_')}
    return type(cls_name, cls_mro, cls_attr)  # 返回一个类


class Config(metaclass=filter_config):  # 指定元类
    """ Singleton `Config` ."""
    _instance = None

    @synchronized
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

    number = 100
    
config = Config()
print(config.NUMBER)  # 100

将类作为元类

import threading


def synchronized(bar):
    """ threading lock for Config"""

    bar.__lock__ = threading.Lock()  # get lock

    def lock_func(*args, **kwargs):
        with bar.__lock__:
            return bar(*args, **kwargs)

    return lock_func


class Filter_config(type):
    def __new__(cls, cls_name, cls_mro, cls_attr):
        cls_attr = {k.upper(): v for k, v in cls_attr.items() if not k.startswith('_')}
        return super(Filter_config, cls).__new__(cls, cls_name, cls_mro, cls_attr)


class Config(metaclass=Filter_config):  # 指定元类
    """ Singleton `Config` ."""
    _instance = None

    @synchronized
    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

    number = 100


config = Config()
print(config.NUMBER)  # 100
posted @ 2020-06-18 18:22  StKali  阅读(546)  评论(0编辑  收藏  举报