• 开发技术--面向对象


    开发|面向对象

    面向对象的学习,可以让我们的代码趋于模块化,规范化,尤其是引入设计模式之后,面向对象的优点就更加明显~~
    希望大家在实际使用中多使用面向对象的思想,enjoy!

    前言

    目前所有的文章思想格式都是:知识+情感。
    知识:对于所有的知识点的描述。力求不含任何的自我感情色彩。
    情感:用我自己的方式,解读知识点。力求通俗易懂,完美透析知识。
    

    正文

    我给本篇文章的定位是回顾自己之前学过的知识点,一定会有我没有涉及到的知识点。我会将我自己学习面向对象之后使用过的,忘记了的,我觉得自己需要掌握的内容,在正文中体现出来~~
    花费了我不少时间,哈哈~~

    必备知识

    1.面向对象直接这么说,是很空的一个概念。尤其是初学者,你说面向对象,根本都不知道是什么,哪怕是学习了很久的人,你问什么是面向对象,十之八九的答案都是,面向对象就是写一个类。。。。terrible~, So, 先一步一步的来了解一下什么是面向对象吧~

    2.看面向对象之前,想函数怎么可以执行,除了一个一个的向下执行,还可以嵌套使用吧!在函数中需要增加一个功能,我想需要更改的地方不一出,改完了,你敢确定没有影响其他的功能吗???答案肯定是,回去检查一下吧!咋们保险一点~

    3.由此面向对象就引出来了,可以简单的理解为面相对象就是解决只使用函数的扩展性问题。(其实,这里有一个问题就是,我写过全是函数的代码,不知你是否写过,又真的有相同体会?)

    4.总结一下,面向对象解决了扩展性问题。由此,咋们一起上了面向对象的车,一路开向面向对象的本质~

    面向对象初识

    接下来将开始,面向对象的第一次实质审查~~

    什么面向对象

    一开始面向对象,大家会说Python里面一切皆对象,是否还记得linux里面一切皆文件~
    补充一下,关于一切皆对象,对象怎么使用???
    1、都可以被引用,x=obj
    2、都可以当作函数的参数传入
    3、都可以当作函数的返回值
    4、都可以当作容器类的元素,例如: l=[func,time,obj,1]

    注意: Python3统一类与类型(数据类型)的概念,类型就是类
    上面这么说有一点空,看一个代码,希望大家可以有所理解~(看到的数据类型都是类)

    In [10]: print(list)
    <class 'list'>
    
    In [11]: print(str)
    <class 'str'>
    
    In [12]: print(dict)
    <class 'dict'>
    

    对象 与 类的关系?

    1.现实生活中,需要先有了一个一个的对象,才可以将对象分为哪一类,哪一类,但是,在自己写程序的时候,需要自己先写到一个类,才可以产生一个的对象,产生对象的过程,称为实例化类

    2.所以,一定是先有类,才可以实例化成为对象

    创建类

    在创建类之前,需要掌握一点基础知识;
    1.类中具有自己的数据,称为数据属性,或者叫数据
    2.类中具有自己的函数,称为函数属性,或者叫方法
    3.看下面创建的类,school属于数据,sleep属于方法

    class Student:
        school = 'Peking University'
    
        def sleep(self):
            print('Student is sleeping.')
    
    

    使用类

    1.使用类,也就如何实例化出对象,根据上例子,student1=Student(),就可以实例化对象。

    2.实例化类,并执行函数

    In [16]: class Student:
        ...:     school = 'Peking University'
        ...:
        ...:     def sleep(self):
        ...:         print('Student is sleeping.')
        ...:
    
    In [17]: student1 = Student()
    
    In [18]: student1.sleep()
    Student is sleeping.
    

    注意:代码加载的时候,就会执行类,产生名称空间存储响应的数据,如果类中的数据属性有another操作,就会被执行。看下面的another操作是print操作。

    In [15]: class Student:
        ...:     school = 'Peking University'
        ...:     print(school)
        ...:
        ...:     def sleep(self):
        ...:         print('Student is sleeping.')
        ...:
    Peking University
    

    类的属性与方法

    在这里将详细的讲一下,一个类中的属性与方法和实例化对象的关系!

    1.类的属性是所有对象共同指向的,共同指向同一内存地址
    验证:

    In [20]: class Student:
        ...:     school = 'Peking University'
        ...:
        ...:     def sleep(self):
        ...:         print('Student is sleeping.')
        ...:
    
    In [21]: student1 = Student()
    
    In [22]: student2 = Student()
    
    In [23]: id(student1.school)
    Out[23]: 1745287897136
    
    In [24]: id(student2.school)
    Out[24]: 1745287897136
    

    2.类的方法是绑定给每一个对象的,对象调用的时候自动传参,bound method 。
    验证:

    In [29]: student1.sleep
    Out[29]: <bound method Student.sleep of <__main__.Student object at 0x000001965B6F95F8>>
    
    In [30]: student2.sleep
    Out[30]: <bound method Student.sleep of <__main__.Student object at 0x000001965B9F2CF8>>
    

    面向对象进阶

    类的命名空间 dict

    1.类的命名空间查看,如下:

    In [33]: Student.__dict__
    Out[33]:
    mappingproxy({'__module__': '__main__',
                  'school': 'Peking University',
                  'sleep': <function __main__.Student.sleep(self)>,
                  '__dict__': <attribute '__dict__' of 'Student' objects>,
                  '__weakref__': <attribute '__weakref__' of 'Student' objects>,
                  '__doc__': None})
    

    2.可以执行下面的代码
    类.__ dict __['name]
    类.name

    In [40]: Student.__dict__['school']
    Out[40]: 'Peking University'
    
    In [41]: Student.school
    Out[41]: 'Peking University'
    

    3.如果想要执行类中的函数属性,需要加上一个对象,来看下面,直接执行会报错,缺少一个对象的参数。解决,传入一个对象即可。

    In [42]: Student.sleep()
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-42-6e9af3d2ac0b> in <module>
    ----> 1 Student.sleep()
    
    TypeError: sleep() missing 1 required positional argument: 'self'
    
    In [43]: Student.sleep(student1)
    Student is sleeping.
    

    init 方法

    1.init作为初始化,可以为每一个对象定制属于自己的属性,只需要在实例化的时候传入即可。

    2.方式一,实例化的时候,直接传参得到对象。

    In [44]: class Student:
        ...:
        ...:     school = 'Peking University'
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:     def sleep(self):
        ...:         print('%s is sleeping.' % self.name)
        ...:
    
    In [45]: student1 = Student('kate', 18)
    
    In [46]: student1.__dict__
    Out[46]: {'name': 'kate', 'age': 18}
    

    3.方式二,在使用方式一的时候,执行了两步,首先是实例化了对象,其次是传入参数,看下面,

    In [48]: class Student:
        ...:
        ...:     school = 'Peking University'
        ...:
        ...:     def sleep(self):
        ...:         print('Student is sleeping.')
        ...:
    
    In [49]: student2 = Student()
    
    In [50]: student2.name = 'kate'
    
    In [51]: student2.age = 18
    
    In [52]: student2.__dict__
    Out[52]: {'name': 'kate', 'age': 18}
    

    bases

    bases的作用就是查看继承的父类有哪些,看下面例子,这个时候出现了一个object的类,但是我们在书写类的时候,并没有继承那个类啊,为什么会多了一个object的类,有疑惑的请移步到新式类与经典类,默认Python3中都是继承object类的,不写也给你默认继承。

    In [54]: Student.__bases__
    Out[54]: (object,)
    

    继承

    1.继承顾名思义就是继承,继承一个或者多个父类,被继承的类叫做基类(超类),继承者叫做子类(派生类)。其中,继承表示的是:什么是什么的关系。

    2.继承之后,实例化的对象的属性查找,需要特别留意。对象的属性,在不继承的时候,先从自己属性查找,再从类中找,找不到就报错。如果有父类,会去父类中找,找不到报错。

    3.总结: 继承的查找顺序----->先自己找,再去类中找,再去父类中找(父类中找也会出现问题,哪一个先找呢,看mro表就知道了~~)

    4.实际看一下继承的书写,(学生继承人这个类)

    In [55]: class People:
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:     def sleep(self):
        ...:         print('%s is sleeping.' % self.name)
        ...:
        ...:
        ...: class Student(People):
        ...:
        ...:     school = 'Peking University'
        ...:
    
    In [56]: student = Student('kate', 18)
    
    In [57]: student.__dict__
    Out[57]: {'name': 'kate', 'age': 18}
    

    派生

    1.在继承中,派生可以得到不同于父类的属性与方法,有两种方式,下面我们一起分别看一下~

    2.指名道姓重用,(不依赖于继承)

    In [65]:
        ...: class People:
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:     def sleep(self):
        ...:         print('%s is sleeping.' % self.name)
        ...:
        ...:
        ...: class Student(People):
        ...:     school = 'Peking University'
        ...:
        ...:     def sleep(self):
        ...:         People.sleep(self)
        ...:         print('Self sleeping....')
        ...:
    
    In [66]: stu = Student('kate', 18)
    
    In [67]: stu.sleep()
    kate is sleeping.
    Self sleeping....
    

    3.使用super(),依赖于继承,使用mro表计算实现继承顺序,在mro也存在一个指针,只要是遇到super,就继续沿着mro表,继续向后找。

    In [68]: class People:
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:     def sleep(self):
        ...:         print('%s is sleeping.' % self.name)
        ...:
        ...:
        ...: class Student(People):
        ...:     school = 'Peking University'
        ...:
        ...:     def sleep(self):
        ...:         super(Student, self).sleep()
        ...:         print('Self sleeping....')
        ...:
    
    In [69]: stu = Student('kate', 18)
    
    In [70]: stu.sleep()
    kate is sleeping.
    Self sleeping....
    

    4,注意:一般的派生都是使用super()。并且更多的应用场景是在init中继承父类的内容,也有自己的内容。一定不要忘记子啊继承查找的时候,遇到super的试试,会继续向后查找的,就不会回到前面在查找了~~

    mro

    mro列表,是查找继承的顺序列表,表示了属性的查找顺序。
    例子:

    In [71]: class A:
        ...:     name = 'A'
        ...:
        ...:
        ...: class B(A):
        ...:     name = "B"
        ...:
        ...:
        ...: class C(A):
        ...:     name = "C"
        ...:
        ...:
        ...: class D(B, C):
        ...:     name = 'D'
        ...:
    
    In [74]: D.mro()
    Out[74]: [__main__.D, __main__.B, __main__.C, __main__.A, object]
    

    新式类

    1.新式类,并不是它有多么的新,哈~
    新式类的讨论,需要分python2与Python3来说了,接下来,没我们一起来看看吧!

    2.Python2的新式类,必须继承object类,继承object的类以及他的子类称为新式类。

    3.Python3,默认所有 的类都是默认继承object类,所以都是新式类,也就是说Python3中没有经典类。

    4.新式类在mro寻继承的时候,遵循广度优先。具体可以查看自己的mro列表。

    经典类

    1.经典类是专门来对于Python2来说的,所以经典类说的是在Python2中, 没有继承object的类。

    2.经典类在继承查询的时候,遵循的是深度优先。

    组合

    1.组合,不需要深入的了解,当一个类不能通过继承实现关系的时候,有需要自己有自己有什么关系的时候,就可以使用派生。此时就,派生出自己的属性与方法。

    In [58]: class People:
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:     def sleep(self):
        ...:         print('%s is sleeping.' % self.name)
        ...:
        ...:
        ...: class Student(People):
        ...:
        ...:     school = 'Peking University'
        ...:
        ...:
        ...: class Course:
        ...:
        ...:     def __init__(self, name, price):
        ...:         self.name = name
        ...:         self.price = price
        ...:
    
    In [59]: student = Student('kate', 18)
    
    In [60]: math = Course('math', 666)
    
    In [61]: student.course = math
    
    In [62]: student.__dict__
    Out[62]: {'name': 'kate', 'age': 18, 'course': <__main__.Course at 0x1965b5c1198>}
    
    In [63]: student.course.name
    Out[63]: 'math'
    
    In [64]: student.course.price
    Out[64]: 666
    

    2.此时表示给对象绑定了一个对象作为他的属性,可以实现代码的重用。

    多态

    Python属于多态的语言,多态更多的体现在方法的调用上,保证多个类可以实现调用同一种方法,实现相同的任务。
    回想,len(),这个内置函数,在执行的时候,可以对字符串,对列表,对元祖获取数据的长度,但是不同的数据类型都是不一样的类,但是实现了同样的函数方法。感觉自己解释不清楚了,看代码吧!

    In [1]: a = 'hello'
    
    In [2]: b = (1, 2, 3)
    
    In [3]: c = [4, 5, 6]
    
    In [4]: len(a)
    Out[4]: 5
    
    In [5]: len(b)
    Out[5]: 3
    
    In [6]: len(c)
    Out[6]: 3
    
    In [8]: a.__len__
    Out[8]: <method-wrapper '__len__' of str object at 0x000002B0701D12D0>
    
    In [9]: b.__len__
    Out[9]: <method-wrapper '__len__' of tuple object at 0x000002B070404EE8>
    
    In [10]: c.__len__
    Out[10]: <method-wrapper '__len__' of list object at 0x000002B0703C0588>
    

    鸭子类型

    鸭子类型,具体的概念我就不解释了!我想解释的是,鸭子模式体现的是多态性,正如上文我所述的len()内置方法。它属于一个比较理论的知识内容,Python语言本身就具体多态性,所以自己在学习的时候,按需进行学习!

    封装

    1.封装作为面向对象的特性之一,就我目前的理解,封装主要是对数据的隐藏,或者是变成自己私有的,由于Python语言的特性,会在内部自动对杠杠开头的变量进行相应的处理,所以正好使用这一特性实现数据隐藏。

    2.具体的实现,隐藏,使前面加 __,此时的 __name 变为 _类名__name在类的加载阶段已经变化了,加上前缀的类名。

    3.看下命名空间,应该可以理解的吧~

    In [11]: class People:
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.__name = name
        ...:         self.__age = age
        ...:
        ...:     def sleep(self):
        ...:         print('%s is sleeping.' % self.__name)
        ...:
    
    In [12]: human = People('kate', 18)
    
    In [13]: human.__dict__
    Out[13]: {'_People__name': 'kate', '_People__age': 18}
    

    4.封装可以将封装起来的数据,自己进行定义,不需要调用者操作与查看,不仅仅是封装数据,还隔离复杂度

    面向对象高阶

    在面向对象高阶,我们开始一步一步的去寻找类的产生源头~~追根溯源。

    抽象类

    1.抽象类的实现,使用的是abc模块,子哎程序的设计模式中可能会使用到,我在设计模式的文章中写过~,主要使用的是Python的abc模块

    2.抽象类的特性:** 只能被继承,不能被实例化。只要是继承抽象类的类,必须重写相应的类方法,名字都必须一样,不然报错~,这在设计模式的时候,就很好的规范了底层的调用接口。**

    3.例子,实现抽象类

    In [17]: import abc
        ...:
        ...:
        ...: class People(metaclass=abc.ABCMeta):
        ...:
        ...:     @abc.abstractmethod
        ...:     def sleep(self):
        ...:         pass
        ...:
        ...:
        ...: class Student(People):
        ...:     school = 'Peking University'
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:     def sleep(self):
        ...:         print('Self sleeping....')
        ...:
    
    In [18]: stu = Student('kate', 18)
    
    In [19]: stu.__dict__
    Out[19]: {'name': 'kate', 'age': 18}
    

    property

    1.property 将访问函数属性变为访问数据属性的方法。原本需要加上括号才可以调用,现在直接就可以进行调用了。统一调用形式不用加括号进行调。用了

    2.可以联动设置的参数,修改:name.setter, 删除:name.deleter

    3.代码实现,了解一下,这个property还是很常用的,

    In [23]: class People:
        ...:
        ...:     def __init__(self, name):
        ...:         self.__name = name
        ...:
        ...:     @property
        ...:     def name(self):
        ...:         return self.__name
        ...:
        ...:     @name.setter
        ...:     def name(self, new_name):
        ...:         self.__name = new_name
        ...:
        ...:
        ...:     @name.deleter
        ...:     def name(self):
        ...:         print('Error .... no permission to delete..')
        ...:
    
    IIn [24]: human = People('kat')
    
    In [26]: human.name
    Out[26]: 'kat'
    
    In [27]: human.name = 'kate'
    
    In [28]: human.name
    Out[28]: 'kate'
    
    In [29]: del human.name
    Error .... no permission to delete..
    

    classmethod

    classmethod属于绑定到类的方法,类可以使用,并且将类作为第一个参数传递进去。此时,可以回忆一下开始面向对象的时候,关于数据属性是所有的对象共同拥有的,所有的函数属性是绑定给每一个对象。**注意:bond method **
    看以下关于classmethod的例子:

    In [30]: class People:
        ...:
        ...:     def __init__(self, name):
        ...:         self.__name = name
        ...:
        ...:     @classmethod
        ...:     def eat(self):
        ...:         print('eat....')
        ...:
        ...:     def sleep(self):
        ...:         print('sleep...')
        ...:
    
    In [31]: peo = People('kate')
    
    In [32]: peo.sleep
    Out[32]: <bound method People.sleep of <__main__.People object at 0x000002B071C3E898>>
    
    In [33]: peo.eat
    Out[33]: <bound method People.eat of <class '__main__.People'>>
    
    In [34]: People.sleep
    Out[34]: <function __main__.People.sleep(self)>
    
    In [35]: People.eat
    Out[35]: <bound method People.eat of <class '__main__.People'>>
    

    staticmethod

    staticmethod表示非绑定方法,对象和类都可以使用,一般没有什么具体的意思,就是一个普通的函数,我在使用的过程中也没有遇到,使用方法类似于classmethod。可以百度了解一下~~

    反射

    反射,这个使用的次数是很多的,尤其是在很多开源的框架(Django 的框架中配置文件中的应用类,内部都是使用getattr)中都使用,这个方法很巧妙,可以字符串与函数关联起来。即可以通过字符串映射到对象的属性。

    hasattr

    hasatrr单独使用是没有啥用的,只是做一个判断,看下对象中是不是存在一个字符串对应的属性。

    getattr

    grtattr 是真正干活的那个,可以进行字符串与函数属性的映射,操作起来相当顺溜~~,一定要多使用,尤其是在开放封闭原则的配置文件,配置参数的时候局可以使用。

    setattr

    setattr一般使用的很少,我在上次学习到现在基本上没有怎么使用。

    delattr

    基本不使用的命令了,没使用过,要是不看到他了,我都忘记了这个了......

    反射示例
    In [39]: class Ftp:
        ...:
        ...:     def start(self):
        ...:         while 1:
        ...:             inp = input('>>>').strip()
        ...:             cmds = inp.split()
        ...:             if hasattr(self, cmds[0]):
        ...:                 func = getattr(self, cmds[0])
        ...:                 func(cmds)
        ...:
        ...:     def put(self, cmds):
        ...:         print(cmds)
        ...:
        ...:     def get(self, cmds):
        ...:         print(cmds)
        ...:
    
    In [40]: f =Ftp()
    
    In [41]: f.start()
    >>>get d.md
    ['get', 'd.md']
    >>>put s.doc
    ['put', 's.doc']
    

    item系列

    item系列,主要是将取出类中的数据属性,变成字典的方式取出,相应的包含getitem, setitem, delitem,实现属性的增删改查。

    In [1]: class Student:
       ...:     def __init__(self, name):
       ...:         self.name = name
       ...:
       ...:     def __getitem__(self, item):
       ...:         print('getitem...')
       ...:         return self.__dict__.get(item)
       ...:
       ...:     def __setitem__(self, key, value):
       ...:         print('setitem.... %s' % value)
       ...:         self.__dict__[key] = value
       ...:
       ...:     def __delitem__(self, key):
       ...:         print('delitem....')
       ...:         del self.__dict__[key]
       ...:
    
    In [2]: stu = Student('kate')
    
    In [3]: stu.name
    Out[3]: 'kate'
    
    In [4]: stu['name']
    getitem...
    Out[4]: 'kate'
    
    In [5]: stu['age']
    getitem...
    
    In [6]: stu['age'] = 18
    setitem.... 18
    
    In [7]: stu['age']
    getitem...
    Out[7]: 18
    
    In [8]: del stu['age']
    delitem....
    
    In [9]: stu.__dict__
    Out[9]: {'name': 'kate'}
    

    str

    类中定义__str__之后,会在打印print的时候,自动触发对应的方法,获得该方法的返回值进行输出,所以str方法必须有返回值,且必须是字符串类型。目前在数据库表中使用过,So,需要学会使用~

    In [10]: class Student:
        ...:
        ...:     def __init__(self, name):
        ...:         self.name = name
        ...:
        ...:     def __str__(self):
        ...:         print('__str__ method')
        ...:         return self.name
        ...:
    
    In [11]: stu = Student('kate')
    
    In [12]: print(stu)
    __str__ method
    kate
    

    iter

    iter方法比较常用,在python中实现了__iter__方法的对象是可迭代的。实际上要想让一个迭代器工作,至少要实现__iter__方法和next方法。

    In [20]: class Test():
        ...:     def __init__(self,data):
        ...:         self.data = data
        ...:
        ...:     def __iter__(self):
        ...:         return self
        ...:     def __next__(self):
        ...:         if self.data > 5:
        ...:             raise StopIteration
        ...:         else:
        ...:             self.data+=1
        ...:             return self.data
        ...:
    
    In [21]: t = Test(3)
    
    In [22]: t
    Out[22]: <__main__.Test at 0x1d9198679e8>
    
    In [23]: for i in t:
        ...:     print(i)
        ...:
    4
    5
    6
    

    del

    1.了解del之前,希望大家可以回一下自己在文件操作的时候,是不是需要打开文件之后必须要关闭文件啊!不想自己的关闭的时候,可以指定使用with语句进行关闭,但是这里就出现了一个问题,程序运行结束打开的文件是不是关闭了??

    2.已经引出del的作用了,del是实现回收操作系统资源,打开文件是操作系统打开文件,并不是自己的英语程序打开,想一下软件是运行在操作系统上的,操作系统建立在硬件的基础之上,获得f文件句柄是是应用程序的变量,记住,文件句柄f是应用程序的一个变量,指向的是操作系统的打开文件的资源,调用read函数的时候,是去操作系统的内存取数据,完了,晕了晕了,可自行百度一下~~

    3.就算del不实现,在程序结束的时候,程序也会自动的时间回收系统资源。在这里瞬间让我想到了numpy中创建一个空数组的时候,会出现非0的空数组,里面包含的资源就是遗留的内存数据~~

    doc

    doc方法是创建类的时候,书写注释之后就可以自动触发该方法执行,并且在这里面可以看到对应的注释内容。

    call

    调用对象的时候,在实例化的对象的后面加上括号,自动触发call执行。还记的怎么触发init方法执行不,是不是实例化对象的时候触发执行~

    In [24]: class School:
        ...:
        ...:     def __init__(self):
        ...:         print('init....')
        ...:
        ...:     def __call__(self, *args, **kwargs):
        ...:         print('call....')
        ...:
        ...:
    
    In [25]: s = School()
    init....
    
    In [26]: s()
    call....
    

    单例模式

    1.单例模式,需要好好地掌握,在程序的设计模式中存在,在实际的使用中会使用,我推荐两种实现单例模式的方法。

    2.第一种实现单例模式,在类模块中直接实例化成为一个对象,在其他模块引入调用的时候,直接调用已经实例化的对象即可,此时就实现了调用的单例模式

    3.在类的内部进行是否已经有实例化的判断,如果已经实例化,就需要将之前的实例化对象返回即可。

    4.单例模式的优点,将参数相同的属性在内存中指向同一个地方,优化的策略。

    5.简单实现单例模式,类内部判断方法

    In [34]: class Mysql:
        ...:     __instance = None
        ...:
        ...:     def __init__(self):
        ...:         self.host = '127.0.0.1'
        ...:         self.port = 3306
        ...:
        ...:     @classmethod
        ...:     def singleton(cls):
        ...:         if not cls.__instance:
        ...:             mysql = cls()
        ...:             cls.__instance = mysql
        ...:         return cls.__instance
        ...:
    
    In [35]: m = Mysql.singleton()
    
    In [36]: n = Mysql.singleton()
    
    In [37]: m
    Out[37]: <__main__.Mysql at 0x1d919a8fda0>
    
    In [38]: n
    Out[38]: <__main__.Mysql at 0x1d919a8fda0>
    

    元类

    终于来到面向对象的高阶中的顶级了~~~让我们一起,看看类的祖宗~

    1.需要了解的东西,元类,顾名思义就是说产生类的类都是元类,其实就是type~

    2.此时我们就可以得到定义类的两种方式: 使用class 或者 使用元类

    3.使用元类定义类的三要素:** 类名 继承类 命名空间**
    类名: 当然是自己起什么就是什么了
    继承类: 没有多继承,那** object 还是需要继承的**
    命名空间: 这个就需要自己结合之前学过一个**内置函数 exec() ** ,这个函数的执行会产生相应的局部命名空间与全局命名空间。

    4.尝试使用元类定义一个类~,看下面的例子:
    注意: 使用exec的时候,如果不将exec内部书写的字符串数据定格书写会报错的,语法不能解析~~~

    In [41]: # 定义类的三要素:类名,类的基类们,类的名称空间
        ...: class_name = 'Chinese'
        ...: class_bases = (object,)
        ...:
        ...: class_body = """
        ...: country='China'
        ...:
        ...: def __init__(self, name):
        ...:     self.name=name
        ...:
        ...: def area(self):
        ...:  print('%s is area' % self.name)
        ...: """
        ...:
        ...: class_dic = {}
        ...: exec(class_body, globals(), class_dic)  # Execute the given source in the context o
        ...: f globals and locals.
        ...:
        ...: Chinese = type(class_name, class_bases, class_dic)  # 元类type定义类,type(name, ba
        ...: ses, dict) -> a new type
        ...:
    
    In [42]: obj1 = Chinese('kate')
    
    In [43]: obj1.name
    Out[43]: 'kate'
    
    In [44]: obj1
    Out[44]: <__main__.Chinese at 0x1d919881be0>
    

    元类控制类的行为

    使用元类控制类的行为,包括控制类的创建行为,与实例化行为。

    1.控制创建行为可以实现, 创建的类必须首字母大写, 必须写注释。。。。,并且可以直接在Mymeta类中添加属性,子类直接可以进行调用。

    In [49]: class Mymeta(type):
        ...:     def __init__(self, class_name, class_bases, class_dic):
        ...:         self.country = 'china'
        ...:
        ...:         if '__doc__' not in class_dic or not class_dic.get('__doc__').strip():
        ...:             raise TypeError('必须为类指定文档注释')
        ...:
        ...:         if not class_name.istitle():
        ...:             raise TypeError('类名首字母必须大写')
        ...:
        ...:         super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        ...:
        ...:
        ...: class Student(object, metaclass=Mymeta):
        ...:     """
        ...:     Student class
        ...:     """
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:
        ...: stu = Student('kate', 18)
        ...:
    
    In [50]: stu.__dict__
    Out[50]: {'name': 'kate', 'age': 18}
    

    2.控制类的实例化行为, 使用的是 触发__call__方法,这个时候就自己创建一个空对象,并调用init函数,返回对象,实现自动触发init执行的内容。

    In [49]: class Mymeta(type):
        ...:     def __init__(self, class_name, class_bases, class_dic):
        ...:         self.country = 'china'
        ...:
        ...:         if '__doc__' not in class_dic or not class_dic.get('__doc__').strip():
        ...:
        ...:         if not class_name.istitle():
        ...:             raise TypeError('类名首字母必须大写')
        ...:
        ...:         super(Mymeta, self).__init__(class_name, class_bases, class_dic)
        ...:
        ...:     def __call__(self, *args, **kwargs):
        ...:         obj = object.__new__(self)  # 实例化Student,产生空对象obj
        ...:         self.__init__(obj, *args, **kwargs)  # 调用Student下的函数__init__,初始化o
        ...: bj
        ...:         return obj  # 返回初始化好了的obj
        ...:
        ...:
        ...: class Student(object, metaclass=Mymeta):
        ...:     """
        ...:     Student class
        ...:     """
        ...:
        ...:     def __init__(self, name, age):
        ...:         self.name = name
        ...:         self.age = age
        ...:
        ...:
        ...: stu = Student('kate', 18)
    
    In [52]: stu.__dict__
    Out[52]: {'name': 'kate', 'age': 18}
    

    结束语

    结束了面向对象的内容,长出一口气,又陷入了新的迷茫~
    面向对象的内容,远不止我所述的这些,还有很多,尤其是面向对象的思想,Python的一切皆对象,底层的很多杠杠方法,我个人觉得,学习重在思考与实践与总结。
    希望大家阅读愉快~

  • 相关阅读:
    sqli-labs第五,六题
    Java 发送邮件
    Java 数据结构
    Java 序列化
    Java 文件注释
    Java 泛型
    Java 网络编程
    网站收藏
    JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
    Java 8 中的 Streams API 详解
  • 原文地址:https://www.cnblogs.com/Kate-liu/p/11243623.html
Copyright © 2020-2023  润新知