zoukankan      html  css  js  c++  java
  • 开发技术--面向对象

    开发|面向对象

    面向对象的学习,可以让我们的代码趋于模块化,规范化,尤其是引入设计模式之后,面向对象的优点就更加明显~~
    希望大家在实际使用中多使用面向对象的思想,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的一切皆对象,底层的很多杠杠方法,我个人觉得,学习重在思考与实践与总结。
    希望大家阅读愉快~

  • 相关阅读:
    Composite in Javascript
    Model Validation in Asp.net MVC
    HttpRuntime.Cache vs. HttpContext.Current.Cache
    Controller Extensibility in ASP.NET MVC
    The Decorator Pattern in Javascript
    The Flyweight Pattern in Javascript
    Model Binding in ASP.NET MVC
    Asp.net MVC
    jQuery Ajax 实例 全解析
    ASP.NET AJAX入门系列
  • 原文地址:https://www.cnblogs.com/Kate-liu/p/11243623.html
Copyright © 2011-2022 走看看