• python面向对象编程


    本文链接:http://blog.csdn.net/sunchengquan/article/details/84673673

     

    面向对象编程概述


    面向过程–怎么做?

    1. 把完成某一个需求的所有步骤,从头到尾逐步实现
    2. 根据开发需求,将某些功能独立的代码封装成一个又一个函数
    3. 最后完成的代码,就是顺序地调用不同的函数

    特点:

    1. 注重步骤与过程,不注重职责分工
    2. 如果需求复杂,代码就会变得很复杂
    3. 开发复杂项目,没有固定的套路,开发难度很大!

    面向对象–谁来做?

    面向对象程序设计(object oriented programming)简称OOP。面向对象编程,使得数据的管理更加合理化和自动化,减少程序出错,少写代码,程序更加容易维护。相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法

    1 在完成某个需求前,首先确定职责–要做的事情(方法)
    2 根据职责确定不同的对象,在对象内部封装不同的方法(多个)
    3 最后完成的代码,就是顺序地让不同的对象调用不同的方法

    万物皆对象

    类和对象是面向对象编程的两个核心概念

    对象优越性

    • 封装:对象将属性和方法进行封装,可以实现信息隐蔽化,用户仅能够使用对象的方法而不能修改这些方法和属性。
    • 继承:获取父对象的属性和能力,再加上自定义的属性和能力,实现代码的重用
    • 包含:一个对象划分几部分,分别进行建模,最后组装成一个完整的对象
    • 多态性:不同对象对同一方法响应不同的行动

    类和对象

    类和对象是面向对象编程的两个核心概念

    是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用

    特征 被称为 属性

    行为 被称为 方法

    类(class)就相当于制造飞机的图纸,就是一个模板 ,是负责创建对象

    对象(object) 是由类创建出来的一个具体存在,可以直接使用

    对象 (object)就是相当于用 图纸 制造的飞机

    类的创建

    三要素:

    1. 类名 ,满足大驼峰命名法
    2. 属性:特征
    3. 方法:行为

    大驼峰命名法:1 每个单词的首字母大写;2 单词与单词之间没有下划线 例如CapWords

    1. 创建出来的 对象 叫做 类 的 实例

    2. 创建对象的 动作 叫做 实例化

    3. 对象的属性 叫做 实例属性

    4. 对象调用的方法 叫做 实例方法

    5. 类的属性 叫做 类属性

    普通方式

    class Person(object):
        pass
    

    class后面紧接着是类名,即Person,类名通常是大写开头的单词,满足大驼峰命名法,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类

    定义好了Person类,就可以根据Person类创建出Person的实例(instance),创建实例是通过类名+()实现的:

    这里实例(instance) 和 对象(object)是一样一样的

    tony = Person()
    print(tony)
    print(Person)
    
    <__main__.Person object at 0x7f5748036470>
    <class '__main__.Person'>
    

    可以看到,变量tony指向的就是一个Person的实例,后面的0x7f574891ff98是内存地址,每个object的地址都不一样,而Person本身则是一个类。

    给一个实例变量绑定属性,比如,给实例tony绑定一个sex属性:

    tony.sex = 'man'
    print(tony.sex)
    
    man
    

    由于类可以起到模板的作用,因此,可以在创建实例的时候,把一些我们认为必须绑定的属性强制填写进去。通过定义一个特殊的__init__方法,在创建实例的时候,就把name,age等属性绑上去:

    提到的第一个方法__init__ ,特殊方法,前后各有两个下划线

    class Person(object):
        def __init__(self, name, weight):
            self.name = name
            self.weight = weight
            
    

    注意到__init__方法的第一个参数永远是self,表示创建的实例本身,而非类,因此,在__init__方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身

    richard = Person("理查德", 60)
    print(richard.name)
    print(richard.weight)
    
    理查德
    60
    

    和普通的函数相比,在类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数。除此之外,类的方法和普通函数没有什么区别,所以,你仍然可以用默认参数、可变参数、关键字参数和命名关键字参数

    特殊方式

    def run(self):
        print("%s 爱跑步,跑步锻炼身体" % self.name)
    
    def eat(self):
        print("%s 是吃货,吃完这顿再减肥" % self.name)
        self.weight += 1
    
    def __init__(self, name, weight):
        self.name = name
        self.weight = weight
    
    Person = type('Person', (object,), {'run':run, 'eat':eat, '__init__':__init__})
    
    mary = Person("mary",70)
    mary.eat()
    
    mary 是吃货,吃完这顿再减肥
    
    • type第一个参数:类名
    • type第二个参数:当前类的基类
    • type第三个参数:类的成员

    类的封装

    对象方法的细节都被封装在类的内部,调用很容易,但却不用知道内部实现的细节

    多个对象属性之间互不干扰

    class Person(object):
        def __init__(self, name, weight):
            self.name = name
            self.weight = weight
    
        def __str__(self):
            return "我的名字叫 %s 体重是 %.2f 公斤" % (self.name, self.weight)
    
        def run(self):
            print("%s 爱跑步,跑步锻炼身体" % self.name)
            self.weight -= 0.5
    
        def eat(self):
            print("%s 是吃货,吃完这顿再减肥" % self.name)
            self.weight += 1
            
    
    

    这里又提到一个特殊方法__str__,如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值

    nina = Person("nina",55)
    print(nina)
    nina.run()
    print(nina)
    
    我的名字叫 nina 体重是 55.00 公斤
    nina 爱跑步,跑步锻炼身体
    我的名字叫 nina 体重是 54.50 公斤
    

    类的继承

    继承概念:

    子类 拥有 父类 的所有 方法 和 属性

    继承语法:

    class 类名(父类名):
        pass
    

    实例演示

    • 不使用继承开发
    class Animal(object):
    
        def eat(self):
            print("吃")
    
        def drink(self):
            print("喝")
    
        def run(self):
            print("跑")
    
        def sleep(self):
            print("睡")
    
    
    class Dog(object):
    
        def eat(self):
            print("吃")
    
        def drink(self):
            print("喝")
    
        def run(self):
            print("跑")
    
        def sleep(self):
            print("睡")
    
        def bark(self):
            print("汪汪叫")
    
    
    wangcai = Dog()
    wangcai.eat()
    wangcai.drink()
    wangcai.run()
    wangcai.sleep()
    wangcai.bark()
    
    吃
    喝
    跑
    睡
    汪汪叫
    
    • 使用继承开发,修改上面的例子
    class Animal(object):
    
        def eat(self):
            print("吃")
    
        def drink(self):
            print("喝")
    
        def run(self):
            print("跑")
    
        def sleep(self):
            print("睡")
    
    
    class Dog(Animal):
    
        def bark(self):
            print("汪汪叫")
    
            
    wangcai = Dog()
    wangcai.eat()
    wangcai.drink()
    wangcai.run()
    wangcai.sleep()
    wangcai.bark()
    
    
    吃
    喝
    跑
    睡
    汪汪叫
    

    专业术语:

    • Dog 类 是 Animal 类的 子类 ,Animal类是Dog类的 父类,Dog类 从 Aniaml类继承
    • Dog 类 是 Animal 类的 派生类,Animal类是Dog类的 基类,Dog类 从 Aniaml类派生

    继承的传递性

    class Animal(object):
    
        def eat(self):
            print("吃")
    
        def drink(self):
            print("喝")
    
        def run(self):
            print("跑")
    
        def sleep(self):
            print("睡")
    
    
    class Dog(Animal):
    
        def bark(self):
            print("汪汪叫")
    
            
    class XiaoTianQuan(Dog):
    
        def fly(self):
            print("我会飞")
            
    xtq = XiaoTianQuan()
    xtq.fly()
    xtq.bark()
    xtq.eat()
    
    我会飞
    汪汪叫
    吃
    
    class Animal(object):
    
        def eat(self):
            print("吃")
    
        def drink(self):
            print("喝")
    
        def run(self):
            print("跑")
    
        def sleep(self):
            print("睡")
    
    class Dog(Animal):
    
        def bark(self):
            print("汪汪叫")
    
    
    class XiaoTianQuan(Dog):
    
        def fly(self):
            print("我会飞")
    
    
    class Cat(Animal):
    
        def catch(self):
            print("抓老鼠")
    
    
    xtq = XiaoTianQuan()
    xtq.fly()
    xtq.bark()
    xtq.eat()
    
    xtq.catch()
    
    
    我会飞
    汪汪叫
    吃
    
    
    
    ---------------------------------------------------------------------------
    
    AttributeError                            Traceback (most recent call last)
    
    <ipython-input-45-c9f717720499> in <module>
         36 xtq.eat()
         37 
    ---> 38 xtq.catch()
    
    
    AttributeError: 'XiaoTianQuan' object has no attribute 'catch'
    

    覆盖父类的方法

    如果子类中,重写了父类的方法

    在使用子类对象调用方法时,会调用子类中重写的方法

    class Dog(Animal):
    
        def bark(self):
            print("汪汪叫")
    
    
    class XiaoTianQuan(Dog):
    
        def fly(self):
            print("我会飞")
    
        def bark(self):
            print("叫得跟神一样...")
    
    
    xtq = XiaoTianQuan()
    xtq.bark()
    
    叫得跟神一样...
    

    多态

    不同对象对同一方法响应不同的行动

    class Animal(object):
        def __init__(self, name): 
            self.name = name
    
        def talk(self): 
            pass 
    
        @staticmethod
        def animal_talk(obj):
            obj.talk()
    
    class Cat(Animal):
        def talk(self):
            print('miao miao')
    
    
    class Dog(Animal):
        def talk(self):
            print('wang wang')
    
    d = Dog("哮天犬")
    d.talk()
    
    c = Cat("加菲")
    c.talk()
    
    # def animal_talk(obj):
    # obj.talk()
    
    Animal.animal_talk(c)
    Animal.animal_talk(d)
    
    e= Animal('xxx')
    e.animal_talk(c)
    e.animal_talk(d)
    
    wang wang
    miao miao
    miao miao
    wang wang
    miao miao
    wang wang
    

    对父类方法进行拓展

    两种解决方法:

    • 调用未绑定的父类方法(通过父类名调用父类的方法)
    • 使用super().父类方法
    import random as r
    
    class Fish(Animal):
        def __init__(self):
            self.x = r.randint(0, 10)
            self.y = r.randint(0, 10)
    
        def move(self):
            self.x -= 1
            print('我的位置是:', self.x, self.y)
    
    class Goldfish(Fish):
        pass
    
    class Carp(Fish):
        pass
    
    class Salmon(Fish):
        pass
    

    shark在保留自身的构造方法__init__同时也继承父类构造方法

    super() -> same as super(__class__, <first argument>)

    class Shark(Fish): 
        def __init__(self):
            self.hungry = True
    #         super().__init__() 
            super(Shark, self).__init__()
        def eat(self):
            if self.hungry:
                print('吃货的梦想就是天天有的吃')
                self.hungry = False
            else:
                print('太撑了,吃不下了')
    
    shark = Shark()
    shark.eat()
    shark.move()
    
    吃货的梦想就是天天有的吃
    我的位置是: 6 7
    
    class Shark(Fish):
        def __init__(self):
            self.hungry = True
            Fish.__init__(self)
            
        def eat(self):
            if self.hungry:
                print('吃货的梦想就是天天有的吃')
                self.hungry = False
            else:
                print('太撑了,吃不下了')
    
    shark = Shark()
    shark.eat()
    shark.move()
    
    吃货的梦想就是天天有的吃
    我的位置是: 0 2
    

    访问限制

    内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问

    在Python中,变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量,所以,不能用__name__这样的变量名。

    class Fish(Animal):
        def __init__(self,name):
            self._name = name
            self.x = r.randint(0, 10)
            self.y = r.randint(0, 10)
    
        def _move(self):
            self.x -= 1
            print('我的位置不可知')
            
        def move(self):
            self.x -= 1
            print('我的位置是:', self.x, self.y)
            
    fish = Fish('美人鱼')
    print(fish._name)
    fish._move()
    
    美人鱼
    我的位置不可知
    

    有些时候,你会看到以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”

    class Fish(Animal):
        def __init__(self,name):
            self.__name = name
            self.x = r.randint(0, 10)
            self.y = r.randint(0, 10)
        
        def __move(self):
            self.x -= 1
            print('我的位置不可知')
            
        def move(self):
            self.x -= 1
            print('我的位置是:', self.x, self.y)
            
    fish = Fish('美人鱼')
    print(fish.__name)
    
    ---------------------------------------------------------------------------
    
    AttributeError                            Traceback (most recent call last)
    
    <ipython-input-116-6f92b2f7d0cd> in <module>
         14 
         15 fish = Fish('美人鱼')
    ---> 16 print(fish.__name)
    
    
    AttributeError: 'Fish' object has no attribute '__name'
    

    双下划线开头的实例变量是不是一定不能从外部访问呢?其实也不是。不能直接访问__name是因为Python解释器对外把__name变量改成了_Fish__name,所以,仍然可以通过_Fish__name来访问__name变量

    print(fish._Fish__name)
    fish._Fish__move()
    
    美人鱼
    我的位置不可知
    

    但是如果外部代码要获取name怎么办?可以给Fish类增加get_name这样的方法:

    class Fish(Animal):
        def __init__(self,name):
            self.__name = name
            self.x = r.randint(0, 10)
            self.y = r.randint(0, 10)
        
        def get_name(self):
            print('我是%s'% self.__name)
        
        def move(self):
            self.x -= 1
            print('我的位置是:', self.x, self.y)
            
    
    fish = Fish('美人鱼')
    fish.get_name()
    
    我是美人鱼
    
    fish.get_name()
    fish.__name = '人鱼小姐'
    print(fish.__name)
    
    我是美人鱼
    人鱼小姐
    

    表面上看,外部代码“成功”地设置了__name变量,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Fish__name,而外部代码给fish新增了一个__name变量。不信试试

    fish.get_name()
    
    我是美人鱼
    

    对父类的访问限制

    class Fish(Animal):
        def __init__(self,name):
            self.__name = name
            self.x = r.randint(0, 10)
            self.y = r.randint(0, 10)
        
        def get_name(self):
            print('我是%s'% self.__name)
        
        def __move(self):
            self.x -= 1
            print('我的位置不可知')
        
        def move(self):
            self.x -= 1
            self.__move()
            print('我的位置是:', self.x, self.y)
    
    class Shark(Fish):
        
        def eat(self):
            self.__move()
    
    fish = Shark("大白鲨")
    fish.eat()
    
    
    ---------------------------------------------------------------------------
    
    AttributeError                            Traceback (most recent call last)
    
    <ipython-input-121-859ae082132f> in <module>
         23 
         24 fish = Shark("大白鲨")
    ---> 25 fish.eat()
    
    
    <ipython-input-121-859ae082132f> in eat(self)
         20 
         21     def eat(self):
    ---> 22         self.__move()
         23 
         24 fish = Shark("大白鲨")
    
    
    AttributeError: 'Shark' object has no attribute '_Shark__move'
    

    在子类的对象方法中,不能调用父类的私有方法

    class Shark(Fish):
        
        def eat(self):
            self.move()
    
    fish = Shark("大白鲨")
    fish.eat()
    
    
    我的位置不可知
    我的位置是: -2 10
    

    在子类的对象方法中,调用父类的公有方法

    获取对象信息

    type()

    基本类型都可以用type()判断:

    print(type(123))
    print(type('str'))
    print(type(None))
    
    <class 'int'>
    <class 'str'>
    <class 'NoneType'>
    

    如果一个变量指向函数或者类,也可以用type()判断:

    print(type(abs))
    print(type(fish))
    
    
    <class 'builtin_function_or_method'>
    <class '__main__.Shark'>
    

    但是type()函数返回的是什么类型呢?它返回对应的Class类型。如果我们要在if语句中判断,就需要比较两个变量的type类型是否相同:

    print(type(123)==type(456))
    print(type(123)==int)
    print(type(123)==str)
    
    True
    True
    False
    

    判断基本数据类型可以直接写intstr等,但如果要判断一个对象是否是函数怎么办?可以使用types模块中定义的常量

    import types
    
    def fn():
        pass
    
    print(type(fn)==types.FunctionType)
    print(type(abs)==types.BuiltinFunctionType)
    print(type(lambda x: x)==types.LambdaType)
    print(type((x for x in range(10)))==types.GeneratorType)
    
    True
    True
    True
    True
    

    isinstance()

    对于class的继承关系来说,使用type()就很不方便。我们要判断class的类型,可以使用isinstance()函数。

    我们回顾上次的例子,如果继承关系是:

    根类object---->Animal---->Fish---->Shark
    
    根类object---->Animal---->Dog---->XiaoTianQuan
    

    那么,isinstance()就可以告诉我们,一个对象是否是某种类型。先创建2种类型的对象:

    a = Fish('x')
    b = Shark('xx')
    d = Dog("哮天犬")
    

    然后,判断:

    print(isinstance(b, Shark))
    
    True
    

    没有问题,因为b变量指向的就是Shark对象。

    再判断:

    print(isinstance(b, Fish))
    
    True
    

    b虽然自身是Shark类型,但由于Shark是从Fish继承下来的,所以,b也还是Fish类型。换句话说,isinstance()判断的是一个对象是否是该类型本身,或者位于该类型的父继承链上。

    因此,我们可以确信,b还是Animal类型:

    print(isinstance(b, Animal))
    print(isinstance(d, Animal))
    
    True
    True
    
    print(isinstance(d, Dog))
    print(isinstance(d, Fish))
    
    True
    False
    

    能用type()判断的基本类型也可以用isinstance()判断:

    print(isinstance('a', str))
    print(isinstance(123, int))
    
    True
    True
    

    并且还可以判断一个变量是否是某些类型中的一种,比如下面的代码就可以判断是否是list或者tuple:

    print(isinstance([1, 2, 3], (list, tuple)))
    print(isinstance((1, 2, 3), (list, tuple)))
    
    True
    True
    

    总是优先使用isinstance()判断类型,可以将指定类型及其子类“一网打尽”

    dir()

    如果要获得一个对象的所有属性和方法,可以使用dir()函数,它返回一个包含字符串的list,比如,获得一个str对象的所有属性和方法:

    print(dir('ABC'))
    
    ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
    

    类似__xxx__的属性和方法在Python中都是有特殊用途的,比如__len__方法返回长度。在Python中,如果你调用len()函数试图获取一个对象的长度,实际上,在len()函数内部,它自动去调用该对象的__len__()方法,所以,下面的代码是等价的:

    print(len('ABC'))
    print('ABC'.__len__())
    
    3
    3
    

    我们自己写的类,如果也想用len(myObj)的话,就自己写一个__len__()方法:

    class Mylen(str):
        def __len__(self):
            return 1
        
    mylen = Mylen()
    len(mylen)
    
    
    
    1
    

    反射

    仅仅把属性和方法列出来是不够的,配合getattr()setattr()delattr()以及hasattr(),我们可以直接操作一个对象的状态

    class MyObject(object):
        def __init__(self):
            self.x = 9
        def power(self):
            return self.x * self.x
    
    obj = MyObject()
    

    紧接着,可以测试该对象的属性:

    print(hasattr(obj, 'x'))
    print(hasattr(obj, 'y'))
    
    setattr(obj, 'y', 19) 
    print(hasattr(obj, 'y'))
    
    print(getattr(obj, 'y'))
    print(obj.y)
    
    delattr(obj, 'y')
    print(getattr(obj, 'y'))
    
    True
    False
    True
    19
    19
    
    
    
    ---------------------------------------------------------------------------
    
    AttributeError                            Traceback (most recent call last)
    
    <ipython-input-165-f0feb18a0533> in <module>
          9 
         10 delattr(obj, 'y')
    ---> 11 print(getattr(obj, 'y'))
    
    
    AttributeError: 'MyObject' object has no attribute 'y'
    

    如果试图获取不存在的属性,会抛出AttributeError的错误:

    getattr(obj, 'z') 
    
    ---------------------------------------------------------------------------
    
    AttributeError                            Traceback (most recent call last)
    
    <ipython-input-166-12ff9bb1afd4> in <module>
    ----> 1 getattr(obj, 'z')
    
    
    AttributeError: 'MyObject' object has no attribute 'z'
    

    可以传入一个default参数,如果属性不存在,就返回默认值:

    print(getattr(obj, 'z', 404))
    
    404
    

    反射实例演示

    def bulk(self):
        print("%s is yelling...." %self.name)
    
    class Dog(object):
        def __init__(self,name):
            self.name = name
    
        def eat(self,food):
            print("%s is eating..."%self.name,food)
    
    
    d = Dog("xiaotianquan")
    choice = input(">>:").strip()
    
    
    if hasattr(d,choice):   
        func= getattr(d,choice) #获取成员对象方法的内存地址
        func('apple')
    else:
        setattr(d,choice,bulk) #d.talk = bulk
        func = getattr(d, choice)
        func(d)
    
    >>:talk
    xiaotianquan is yelling....
    

    多重继承

    py3 经典类和新式类都是统一按广度优先来继承的

    按广度,D继承B,没有再继承C,没有最后继承A

    class A(object):
        def __init__(self):
            print("A")
    
    
    class B(A):
    
        def __init__(self):
            print("B")
    
        # def test(self):
        #     print("B --- test 方法")
        #
        # def demo(self):
        #     print("B --- demo 方法")
    
    
    class C(A):
        def __init__(self):
            print("C")
    
        def test(self):
            print("C --- test 方法")
    
        def demo(self):
            print("C --- demo 方法")
    
    
    class D(B, C):
        """多继承可以让子类对象,同时具有多个父类的属性和方法"""
        pass
    
    
    # 创建子类对象
    d = D()
    
    # 确定D类对象调用方法的顺序
    print(D.__mro__)
    
    B
    (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
    
    d.test()
    d.demo()
    
    C --- test 方法
    C --- demo 方法
    

    py2 经典类是按深度优先来继承的,新式类是按广度优先来继承的

    新式类经典类 在多继承时 —— 会影响到方法的搜索顺序

    为了保证编写的代码能够同时在 Python 2.xPython 3.x 运行!
    今后在定义类时,如果没有父类,建议统一继承自 object

    类的属性

    复习概念:

    1. 创建出来的 对象 叫做 类 的 实例

    2. 创建对象的 动作 叫做 实例化

    3. 对象的属性 叫做 实例属性, 定义时使用self.属性名

    4. 对象调用的方法 叫做 实例方法

    5. 类属性 就是针对 类对象 定义的属性, 同一个类的所有实例所共有的,引用时要使用 类名.类变量名

      • 类属性 用于记录 与这个类相关 的特征
    6. 类方法 就是针对 类对象 定义的方法

      • 类方法 内部可以直接访问 类属性 或者调用其他的 类方法

    实例变量(实例属性)与类变量(类属性)的区别:

    实例变量存在每个实例的内存里,每个实例之间相互不影响

    类变量存在类的内存里,相当于所有的实例共享这个内存

    实例演示

    class Property(object):
        class_name = 'Property'
        def __init__(self, x=0):
            self.x = x
            print('初始化实例')
        
        def class_info(self):
            print("类变量值:", Property.class_name)
            print('实例变量值:',self.x)
            
        def change(self, x):
            print('修改Property实例变量为', x)
            self.x = x
            
        def change_property(self, name):
            print('修改Property类变量为', name)
            Property.class_name = name
    
    
    sun = Property() 
    
    初始化实例
    
    sun.class_info()
    
    类变量值: changed
    实例变量值: 0
    
    sun.change(3)
    
    修改Property实例变量为 3
    
    sun.class_info()
    
    类变量值: changed
    实例变量值: 3
    
    sun.change_property("changed")
    
    修改Property类变量为 changed
    
    sun.class_info()
    
    类变量值: changed
    实例变量值: 3
    

    假设类变量与实例变量重名,会出现什么情况?

    class Property(object):
        name = 'Property'
        def __init__(self, x=0):
            self.x = x
            print('初始化实例')
        
        def class_info(self):
            print("类变量值:", Property.name)
            print('实例变量值:',self.x)
            
        def change(self, x):
            print('修改Property实例变量为', x)
            self.x = x
            
        def change_property(self, name):
            print('修改Property类变量为', name)
            Property.name = name
    
    p = Property()
    
    初始化实例
    

    打印name属性,因为实例并没有name属性,所以会继续查找class的name属性

    print(p.name)
    
    Property
    

    打印类的name属性

    print(Property.name) 
    
    Property
    

    给实例绑定name属性

    p.name = 'richard' 
    

    由于实例属性优先级比类属性高,因此,它会屏蔽掉类的name属性

    print(p.name) 
    
    richard
    

    但是类属性并未消失,用Property.name仍然可以访问

    print(Property.name) 
    
    Property
    

    如果删除实例的name属性

    del p.name 
    

    再次调用p.name,由于实例的name属性没有找到,类的name属性就显示出来了

    print(p.name)
    
    Property
    

    在编写程序的时候,千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性

    实例练习

    为了统计学生人数,可以给Student类增加一个类属性,每创建一个实例,该属性自动增加:

    class Student(object):
        count = 0
    
        def __init__(self, name):
            self.name = name
            Student.count += 1     
            
    

    测试

    if Student.count != 0:
        print('测试失败!')
    else:
        bart = Student('Bart')
        if Student.count != 1:
            print('测试失败!')
        else:
            lisa = Student('Bart')
            if Student.count != 2:
                print('测试失败!')
            else:
                print('Students:', Student.count)
                print('测试通过!')
    
    Students: 2
    测试通过!
    

    类的方法

    __init__

    初始化方法(构造方法)

    class Cat(object):
        def __init__(self, new_name):
            print("这是一个初始化方法")
            self.name = new_name
    
    
    tom = Cat("Tom")
    
    这是一个初始化方法
    

    __del__

    析构方法

    应用场景

    • __init__ 改造初始化方法,可以让创建对象更加灵活
    • __del__ 如果希望在对象被销毁前,再做一些事情,可以考虑一下 __del__ 方法
    class Cat(object):
    
        def __init__(self, new_name):
            self.name = new_name
            print("%s 来了" % self.name)
    
        def __del__(self):
            print("%s 我去了" % self.name)
    
    tom = Cat("Tom")
    # del tom
    
    Tom 来了
    Tom 我去了
    

    __doc__

    表示类的描述信息

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            print("%s 来了" % self.name)
    
        def __del__(self):
            print("%s 我去了" % self.name)
    
    print(Cat.__doc__)
    
    描述类信息,这是一个喵喵
    

    __str__

    Python 中,使用 print 输出 对象变量,默认情况下,会输出这个变量 引用的对象由哪一个类创建的对象,以及 在内存中的地址十六进制表示

    如果在开发中,希望使用 print 输出 对象变量 时,能够打印 自定义的内容,就可以利用 __str__ 这个内置方法了

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            print("%s 来了" % self.name)
    
        def __del__(self):
              print("%s 我去了" % self.name)
                
        def __str__(self):
             # 必须返回一个字符串
            return "我是小猫[%s]" % self.name
    
    tom = Cat("Tom")
    print(tom)
    
    Tom 来了
    Tom 我去了
    我是小猫[Tom]
    

    __call__

    对象后面加括号,触发执行,可调用

    注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            print("%s 来了" % self.name)
    
        def __del__(self):
              print("%s 我去了" % self.name)
                
        def __str__(self):
             # 必须返回一个字符串
            return "我是小猫[%s]" % self.name
        
        def __call__(self):
            print("执行__call__方法")
            
    
    
    tom = Cat("Tom") 
    tom()
    
    Tom 来了
    Tom 我去了
    执行__call__方法
    

    __class__

    表示当前操作的对象的类是什么

    print(tom.__class__)
    
    <class '__main__.Cat'>
    

    __module__

    表示当前操作的对象在那个模块

    __main__ 表示没有从别的模块中导入

    print(tom.__module__) 
    
    __main__
    
    lib = __import__('lib.test')
    tom = lib.test.Person()
    print(tom.__module__) 
    
    lib.test
    

    __dict__

    查看类或对象中的所有成员 ,输出结果是什么格式?

    # 获取类的成员,即:静态字段、方法、
    print(Cat.__dict__)
    
    {'__module__': '__main__', '__doc__': '描述类信息,这是一个喵喵', '__init__': <function Cat.__init__ at 0x7f5733fbe620>, '__del__': <function Cat.__del__ at 0x7f5733fbe510>, '__str__': <function Cat.__str__ at 0x7f5733fbe1e0>, '__call__': <function Cat.__call__ at 0x7f5733fbe268>, '__dict__': <attribute '__dict__' of 'Cat' objects>, '__weakref__': <attribute '__weakref__' of 'Cat' objects>}
    
    # 获取 对象tom 的成员
    tom = Cat("Tom") 
    print(tom.__dict__)
    
    Tom 来了
    Tom 我去了
    {'name': 'Tom'}
    

    __iter__

    如果类加上__iter__, 实例化后的对象是一个可迭代对象

    from collections import Iterable
    
    print(isinstance('abc',Iterable))
    print(isinstance([],Iterable))
    print(isinstance({},Iterable))
    
    True
    True
    True
    
    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            print("%s 来了" % self.name)
    
        def __del__(self):
              print("%s 我去了" % self.name)
                
        def __str__(self):
             # 必须返回一个字符串
            return "我是小猫[%s]" % self.name
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
    tom = Cat("Tom")
    
    Tom 来了
    Tom 我去了
    
    print(isinstance(tom ,Iterable))
    print(isinstance(tom ,Iterator))
    
    True
    False
    

    __next__

    把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()__next__()

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            self.num = 0
            print("%s 来了" % self.name)
    
        def __del__(self):
              print("%s 我去了" % self.name)
                
        def __str__(self):
             # 必须返回一个字符串
            return "我是小猫[%s]" % self.name
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d个小猫'%(self.name,self.num))
            
    tom = Cat("Tom")
    
    
    Tom 来了
    Tom 我去了
    
    from collections import Iterable
    from collections import Iterator
    
    print(isinstance(tom ,Iterable))
    print(isinstance(tom ,Iterator))
    
    True
    True
    
    next(tom)
    
    Tom 产下了第1个小猫
    
    next(tom)
    
    Tom 产下了第2个小猫
    
    for i in tom:
        pass
    
    Tom 产下了第3个小猫
    Tom 产下了第4个小猫
    Tom 产下了第5个小猫
    Tom 产下了第6个小猫
    
    tom[0]
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-299-fb6fc0384b04> in <module>
    ----> 1 tom[0]
    
    
    TypeError: 'Cat' object does not support indexing
    

    __getitem__

    要表现得像list那样按照下标取出元素,需要实现__getitem__()方法:

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            self.num = 0
            print("%s 来了" % self.name)
    
        def __del__(self):
              print("%s 我去了" % self.name)
                
        def __str__(self):
             # 必须返回一个字符串
            return "我是小猫[%s]" % self.name
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
            
        def __getitem__(self, n):
            a = "第1个小猫"
            for x in range(n):
                a = '第%d个小猫'% (x+2)
            return a
            
    tom = Cat("Tom")
    
    Tom 来了
    Tom 我去了
    
    print(tom[0])
    
    第1个小猫
    
    print(tom[8])
    
    第9个小猫
    
    tom[0:2]
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-305-ca1631d2d9b8> in <module>
    ----> 1 tom[0:2]
    
    
    <ipython-input-301-07ecfbc21c8b> in __getitem__(self, n)
         27     def __getitem__(self, n):
         28         a = "第1个小猫"
    ---> 29         for x in range(n):
         30             a = '第%d个小猫'% (x+2)
         31         return a
    
    
    TypeError: 'slice' object cannot be interpreted as an integer
    

    报错,原因是__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            self.num = 0
            print("%s 来了" % self.name)
    
        def __del__(self):
              print("%s 我去了" % self.name)
                
        def __str__(self):
             # 必须返回一个字符串
            return "我是小猫[%s]" % self.name
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
            
        def __getitem__(self, n):
            if isinstance(n, int): # n是索引
                a = "第1个小猫"
                for x in range(n):
                    a = '第%d个小猫'% (x+2)
                return a
            elif isinstance(n, slice): # n是切片
                start = n.start
                stop = n.stop
                if start is None:
                    start = 0
                a = "第1个小猫"
                L = []
                for x in range(stop):
                    a = '第%d个小猫'% (x+2)
                    L.append(a)
                return L
        
    
    tom = Cat("Tom")
    print(tom[8])
    print(tom[:2])
    
    Tom 来了
    Tom 我去了
    第9个小猫
    ['第2个小猫', '第3个小猫']
    

    __setitem____delitem__

    __setitem__()方法,把对象视作list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self, new_name):
            self.name = new_name
            self.num = 0
            print("%s 来了" % self.name)
    
        def __del__(self):
              print("%s 我去了" % self.name)
                
        def __str__(self):
             # 必须返回一个字符串
            return "我是小猫[%s]" % self.name
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
            
        def __getitem__(self, n):
            if isinstance(n, int): # n是索引
                return self.__dict__[n]
            elif isinstance(n, slice): # n是切片
                start = n.start
                stop = n.stop
                if start is None:
                    start = 0
                L = [self.__dict__[i] for i in range(start,stop)]
                return L
        
        def __setitem__(self, key, value):
            self.__dict__[key] = value
            print("添加", value, "成功")
            
        def __delitem__(self, key):
            if key in self.__dict__:
                print("删除", self.__dict__[key], "成功")
                self.__dict__.pop(key)
            
            
    tom = Cat("Tom")      
            
    
    Tom 来了
    
    for x in range(8):
        tom[x+1] = '第%d个小猫'% (x+1)
    
    
    添加 第1个小猫 成功
    添加 第2个小猫 成功
    添加 第3个小猫 成功
    添加 第4个小猫 成功
    添加 第5个小猫 成功
    添加 第6个小猫 成功
    添加 第7个小猫 成功
    添加 第8个小猫 成功
    
    for i in range(1,9):
        print(tom[i])
    
    第1个小猫
    第2个小猫
    第3个小猫
    第4个小猫
    第5个小猫
    第6个小猫
    第7个小猫
    第8个小猫
    
    print(tom[6:8])
    
    
    ['第6个小猫', '第7个小猫']
    
    del tom[6]
    print(tom[7])
    print(tom[6])
    
    删除 第6个小猫 成功
    第7个小猫
    
    
    
    ---------------------------------------------------------------------------
    
    KeyError                                  Traceback (most recent call last)
    
    <ipython-input-410-20201facdff5> in <module>
          1 del tom[6]
          2 print(tom[7])
    ----> 3 print(tom[6])
    
    
    <ipython-input-405-9dc9bf9cea6c> in __getitem__(self, n)
         27     def __getitem__(self, n):
         28         if isinstance(n, int): # n是索引
    ---> 29             return self.__dict__[n]
         30         elif isinstance(n, slice): # n是切片
         31             start = n.start
    
    
    KeyError: 6
    

    __getattr__

    如果属性查找(attribute lookup)在实例以及对应的类中(通过__dict__)失败, 那么会调用到类的__getattr__函数, 如果没有定义这个函数,那么抛出AttributeError异常。由此可见,__getattr__一定是作用于属性查找的最后一步,兜底

    tom.sun
    
    ---------------------------------------------------------------------------
    
    AttributeError                            Traceback (most recent call last)
    
    <ipython-input-94-4ce2bf3c8da4> in <module>
    ----> 1 tom.sun
    
    
    AttributeError: 'Cat' object has no attribute 'sun'
    
    
    class Cat(dict):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, *args, **kwargs):
            super(Cat, self).__init__(*args, **kwargs)
            self.name = name
            self.num = 0
            self.value = ''
    
        def __del__(self):
              pass
    
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
            
        def __getitem__(self, n):
            if isinstance(n, int): # n是索引
                return self.__dict__[n]
            elif isinstance(n, slice): # n是切片
                start = n.start
                stop = n.stop
                if start is None:
                    start = 0
                L = [self.__dict__[i] for i in range(start,stop)]
                return L
        
        def __setitem__(self, key, value):
            self.__dict__[key] = value
            print("添加", value, "成功")
            
        def __delitem__(self, key):
            if key in self.__dict__:
                print("删除", self.__dict__[key], "成功")
                self.__dict__.pop(key)
        
        def __getattr__(self, name):
            self.value =  self.get(name,'sunchengquan')
            if isinstance(self.value, dict):
                self.value =Cat(self.name ,self.value)
            return self.value
         
            
    
    tom = Cat("Tom")  
    print(tom.sun)
    
    sunchengquan
    

    可以像访问属性一样访问dict中的键值对

    tom = Cat("Tom",ab={'a': 1,'b':2}, c=True)  
    
    print(tom.ab)
    print(tom.ab.b)
    print(tom.c)
    
    {'a': 1, 'b': 2}
    2
    True
    

    __add__; __sub__ ; __mul____truediv__ ; __mod__

    加运算,减运算,乘运算, 除运算,: 求余运算

    class Cat(dict):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, a, b, *args, **kwargs):
            super(Cat, self).__init__(*args, **kwargs)
            self.name = name
            self.num = 0
            self.value = ''
            self.a = a
            self.b = b
    
        def __del__(self):
              pass
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
            
        def __getitem__(self, n):
            if isinstance(n, int): # n是索引
                return self.__dict__[n]
            elif isinstance(n, slice): # n是切片
                start = n.start
                stop = n.stop
                if start is None:
                    start = 0
                L = [self.__dict__[i] for i in range(start,stop)]
                return L
        
        def __setitem__(self, key, value):
            self.__dict__[key] = value
            print("添加", value, "成功")
            
        def __delitem__(self, key):
            if key in self.__dict__:
                print("删除", self.__dict__[key], "成功")
                self.__dict__.pop(key)
        
        def __getattr__(self, name):
            self.value =  self.get(name,'sunchengquan')
            if isinstance(self.value, dict):
                self.value =Cat(self.name ,self.a, self.b, self.value)
            return self.value
        
        def __add__(self,other):
            return Cat(self.name, self.a + other.a, self.b + other.b)
            
        def __sub__(self,other):
            return Cat(self.name, self.a - other.a, self.b - other.b)
            
        def __mul__(self,other):
            return Cat(self.name, self.a * other.a, self.b * other.b)
            
        def __truediv__(self,other):
            return Cat(self.name, self.a / other.a, self.b / other.b)
        
        def __mod__(self,other):
            return Cat(self.name, self.a % other.a, self.b % other.b)
        
        def __str__(self):
            return '(%d, %d)' % (self.a, self.b)
    
    tom1 = Cat("Tom",8, 4)  
    tom2 = Cat("Tom",3, 4)  
    print(tom1+tom2)
    print(tom1-tom2)
    print(tom1*tom2)
    print(tom1/tom2)
    print(tom1%tom2)
    
    (11, 8)
    (5, 0)
    (24, 16)
    (2, 1)
    (2, 0)
    

    __cmp__

    对 int、str 等内置数据类型排序时,Python的 sorted() 按照默认的比较函数 cmp 排序,但是,如果对一组 Student 类的实例排序时,就必须提供我们自己的特殊方法 __cmp__()

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, a, b, *args, **kwargs):
            super(Cat, self).__init__(*args, **kwargs)
            self.name = name
            self.num = 0
            self.value = ''
            self.a = a
            self.b = b
    
        def __del__(self):
              pass
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
        
        def __add__(self,other):
            return Cat(self.name, self.a + other.a, self.b + other.b)
            
        def __sub__(self,other):
            return Cat(self.name, self.a - other.a, self.b - other.b)
            
        def __mul__(self,other):
            return Cat(self.name, self.a * other.a, self.b * other.b)
            
        def __truediv__(self,other):
            return Cat(self.name, self.a / other.a, self.b / other.b)
        
        def __mod__(self,other):
            return Cat(self.name, self.a % other.a, self.b % other.b)
        
        def __cmp__(self, s):
            if(self.a<s.a):
                return 1
            if(self.a>s.a):
                return -1
            if(self.a==s.a):
                if(self.name>s.name):
                    return 1;
                if(self.name<s.name):
                    return -1
                return 0
        
        def __str__(self):
            return '(%d, %d)' % (self.a, self.b)
        
    
    L = [Cat('Tim', 99,1), Cat('Bob', 88,1), Cat('Alice', 99,1),100 ,"sun"]
    print(sorted(L))
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-303-29f737bd28d8> in <module>
          1 L = [Cat('Tim', 99,1), Cat('Bob', 88,1), Cat('Alice', 99,1),100 ,"sun"]
    ----> 2 print(sorted(L))
    
    
    TypeError: '<' not supported between instances of 'Cat' and 'Cat'
    

    __lt__; __le__;__gt__; __ge__;__eq__;__ne__

    上面没有解决的问题:

    TypeError: '<' not supported between instances of 'Cat' and 'Cat'
    
    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, a, b, *args, **kwargs):
            self.name = name
            self.num = 0
            self.value = ''
            self.a = a
            self.b = b
    
        def __del__(self):
              pass
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
            
        
        def __add__(self,other):
            return Cat(self.name, self.a + other.a, self.b + other.b)
            
        def __sub__(self,other):
            return Cat(self.name, self.a - other.a, self.b - other.b)
            
        def __mul__(self,other):
            return Cat(self.name, self.a * other.a, self.b * other.b)
            
        def __truediv__(self,other):
            return Cat(self.name, self.a / other.a, self.b / other.b)
        
        def __mod__(self,other):
            return Cat(self.name, self.a % other.a, self.b % other.b)
        
        def __lt__(self,other):
            if self.a < other.a:
                return True
            else:
                return False
        
        def __le__(self,other):
            if self.a <= other.a:
                return True
            else:
                return False
            
        def __ge__(self,other):
            if self.a >= other.a:
                return True
            else:
                return False
            
        def __gt__(self,other):
            if self.a > other.a:
                return True
            else:
                return False
            
        def __eq__(self,other):
            if self.a == other.a:
                return True
            else:
                return False
        def __ne__(self,other):
            if self.a != other.a:
                return True
            else:
                return False
        
        def __cmp__(self, other):
            if(self.a < other.a):
                return 1
            if(self.a > other.a):
                return -1
            if(self.a == other .a):
                if(self.name > other.name):
                    return 1;
                if(self.name < other.name):
                    return -1
                return 0
        
        def __str__(self):
            return '(%d, %d)' % (self.a, self.b)
        
    
    c1 = Cat('Tim', 97, 1)
    c2 = Cat('Bob', 88, 1)
    c3 = Cat('Alice', 99, 1)
    print(c1 < c2)
    print(c1 <= c2)
    print(c1 == c2)
    print(c1 != c2)
    
    False
    False
    False
    True
    
    l = [c1,c2,c3]
    print(sorted(l))
    
    [<__main__.Cat object at 0x7f3cbc124f28>, <__main__.Cat object at 0x7f3cbc124208>, <__main__.Cat object at 0x7f3cbc124128>]
    

    __repr__

    上面没有解决的问题

    [<__main__.Cat object at 0x7f3cbc124f28>, <__main__.Cat object at 0x7f3cbc124208>, <__main__.Cat object at 0x7f3cbc124128>]
    

    __repr____str__这两个方法都是用于显示的,__str__是面向用户的,而__repr__面向程序员

    重构__repr__方法后,不管直接输出对象还是通过print打印的信息都按我们__repr__方法中定义的格式进行显示了

    重构__str__ 你会发现,直接输出对象ts时并没有按我们__str__方法中定义的格式进行输出,而用print输出的信息却改变了

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, a, b, *args, **kwargs):
            self.name = name
            self.num = 0
            self.value = ''
            self.a = a
            self.b = b
    
        def __del__(self):
              pass
        
        def __call__(self):
            print("执行__call__方法")
            
        def __iter__(self):
            return self 
    
        def __next__(self):
            self.num += 1
            if self.num > 6:
                raise StopIteration()
            print('%s 产下了第%d小猫'%(self.name,self.num))
            
        
        def __add__(self,other):
            return Cat(self.name, self.a + other.a, self.b + other.b)
            
        def __sub__(self,other):
            return Cat(self.name, self.a - other.a, self.b - other.b)
            
        def __mul__(self,other):
            return Cat(self.name, self.a * other.a, self.b * other.b)
            
        def __truediv__(self,other):
            return Cat(self.name, self.a / other.a, self.b / other.b)
        
        def __mod__(self,other):
            return Cat(self.name, self.a % other.a, self.b % other.b)
        
        def __lt__(self,other):
            if self.a < other.a:
                return True
            else:
                return False
        
        def __le__(self,other):
            if self.a <= other.a:
                return True
            else:
                return False
            
        def __ge__(self,other):
            if self.a >= other.a:
                return True
            else:
                return False
            
        def __gt__(self,other):
            if self.a > other.a:
                return True
            else:
                return False
            
        def __eq__(self,other):
            if self.a == other.a:
                return True
            else:
                return False
        def __ne__(self,other):
            if self.a != other.a:
                return True
            else:
                return False
        
        def __cmp__(self, other):
            if self.a < other.a:
                return 1
            if self.a > other.a:
                return -1
            if self.a == other.a:
                return 0
        
        def __str__(self):
            return '(%s %d, %d)' % (self.name, self.a, self.b)
        
        __repr__ = __str__
    
    l = [Cat('Tim', 97, 1), Cat('Bob', 88, 1),  Cat('Mary', 99, 1 ), Cat('Alice', 99, 2)]
    print(sorted(l))
    
    [(Bob 88, 1), (Tim 97, 1), (Mary 99, 1), (Alice 99, 2)]
    

    __bool__

    对类进行布尔值判断

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, a, b, *args, **kwargs):
            self.name = name
            self.a = a
            self.b = b
        
        def __le__(self,other):
            if self.a <= other.a:
                return True
            else:
                return False
            
        def __ge__(self,other):
            if self.a >= other.a:
                return True
            else:
                return False
            
        def __gt__(self,other):
            if self.a > other.a:
                return True
            else:
                return False
            
        def __eq__(self,other):
            if self.a == other.a:
                return True
            else:
                return False
        def __ne__(self,other):
            if self.a != other.a:
                return True
            else:
                return False
        
        def __str__(self):
            return '(%s %d, %d)' % (self.name, self.a, self.b)
        
        __repr__ = __str__
        
        def __bool__(self):
            return bool(self.a > 5)
        
        
        
    
    tom1 = Cat("Tom",1,2)
    tom2 = Cat("Tom",8,2)
    
    print(tom1)
    print(tom2)
        
    if tom1:
        print("tom1")
    if tom2:
        print("tom2")
    
    (Tom 1, 2)
    (Tom 8, 2)
    tom2
    

    @staticmethod

    静态方法

    只是名义上归类管理, 实际上在静态方法里访问不了类或实例中的任何属性

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, *args, **kwargs):
            self.name = name
        
        @staticmethod #实际上跟类没什么关系了
        def eat(self):
            print("%s is eating %s" %(self.name,'dd'))
    
        def talk(self):
            print("%s is talking"% self.name)
            
    tom = Cat("Tom")
    tom.eat()
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-344-688b08415254> in <module>
         12 
         13 tom = Cat("Tom")
    ---> 14 tom.eat()
    
    
    TypeError: eat() missing 1 required positional argument: 'self'
    

    怎么解决上面的问题?

    调用时主动传递实例本身给eat方法,即tom.eat(tom)

    tom.eat(tom)
    
    Tom is eating dd
    

    在eat方法中去掉self参数,但这也意味着,在eat中不能通过self.调用实例中的其它变量了

    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, *args, **kwargs):
            self.name = name
        
        @staticmethod #实际上跟类没什么关系了
        def eat():
            print("%s is eating %s" %(self.name,'dd'))
    
        def talk(self):
            print("%s is talking"% self.name)
    tom = Cat("Tom")
    tom.eat()
    
    ---------------------------------------------------------------------------
    
    NameError                                 Traceback (most recent call last)
    
    <ipython-input-347-116f9837bbbc> in <module>
         11         print("%s is talking"% self.name)
         12 tom = Cat("Tom")
    ---> 13 tom.eat()
    
    
    <ipython-input-347-116f9837bbbc> in eat()
          6     @staticmethod #实际上跟类没什么关系了
          7     def eat():
    ----> 8         print("%s is eating %s" %(self.name,'dd'))
          9 
         10     def talk(self):
    
    
    NameError: name 'self' is not defined
    
    class Cat(object):
        """描述类信息,这是一个喵喵"""
        def __init__(self,name, *args, **kwargs):
            self.name = name
        
        @staticmethod #实际上跟类没什么关系了
        def eat():
            print("eating %s" %('dd'))
    
        def talk(self):
            print("%s is talking"% self.name)
    tom = Cat("Tom")
    tom.eat()
    
    eating dd
    

    @classmethod

    类方法:只能访问类变量,不能访问实例变量

    class Cat(object):
        
        name = "huazai"
        def __init__(self,name):
            self.name = name
       
        @classmethod
        def eat(self):
            print("%s is eating %s" %(self.name,'dd'))
    
        def talk(self):
            print("%s is talking"% self.name)
    
    
    cat = Cat("Tom")
    cat.eat()
    
    huazai is eating dd
    
    class Cat(object):
        
        name = "huazai"
        def __init__(self,name):
            self.name = name
       
        @staticmethod
        def show_info():
            print("帮助信息:
    	这是一个大肥喵
    	主人是xxxxxxxx")
    
        @classmethod
        def eat(cls):
            print("%s is eating %s" %(cls.name,'dd'))
    
        def talk(self):
            print("%s is talking"% self.name)
    
    
    cat = Cat("Tom")
    cat.eat()
    cat.show_info()
    
    huazai is eating dd
    帮助信息:
    	这是一个大肥喵
    	主人是xxxxxxxx
    

    @property

    属性方法

    把一个方法变成一个静态属性

    调用会出以下错误, 说NoneType is not callable, 因为eat此时已经变成一个静态属性了, 不是方法了, 想调用已经不需要加()号了,直接cat.eat就可以了

    class Cat(object):
        
        name = "huazai"
        def __init__(self,name):
            self.name = name
       
        @property
        def eat(self):
            print("%s is eating %s" %(self.name,'dd'))
    
        def talk(self):
            print("%s is talking"% self.name)
    
    cat = Cat("Tom")
    cat.eat()     
    
    Tom is eating dd
    
    
    
    ---------------------------------------------------------------------------
    
    TypeError                                 Traceback (most recent call last)
    
    <ipython-input-352-254f28ff15c1> in <module>
         13 
         14 cat = Cat("Tom")
    ---> 15 cat.eat()
    
    
    TypeError: 'NoneType' object is not callable
    
    cat = Cat("Tom")
    cat.eat
    
    Tom is eating dd
    

    修改属性值

    class Cat(object):
        '''这个类是描述狗这个对象的'''
    
        def __init__(self,name):
    
            self.name = name
            self.__food = None
    
        @property 
        def eat(self):
            print("%s is eating %s" %(self.name,self.__food))
        
        @eat.setter
        def eat(self,food):
            print("set to food:",food)
            self.__food = food
        
        @eat.deleter
        def eat(self):
            del self.__food
            print("删完了")
    
        def talk(self):
            print("%s is talking"% self.name)
            
    
    cat = Cat("Tom")
    cat.eat
    
    Tom is eating None
    
    cat.eat = 'fish'
    
    set to food: fish
    
    cat.eat
    
    Tom is eating fish
    

    案例演示

    把一个方法变成静态属性有什么卵用呢?既然想要静态变量,那直接定义成一个静态变量不就得了么?well, 以后你会需到很多场景是不能简单通过 定义 静态属性来实现的, 比如 ,你想知道一个航班当前的状态,是到达了、延迟了、取消了、还是已经飞走了, 想知道这种状态你必须经历以下几步:

    1. 连接航空公司API查询
    2. 对查询结果进行解析
    3. 返回结果给你的用户

    因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用时,其实它都要经过一系列的动作才返回你结果,但这些动作过程不需要用户关心, 用户只需要调用这个属性就可以

    class Flight(object):
        def __init__(self,name):
            self.flight_name = name
    
        def checking_status(self):
            print("checking flight %s status " % self.flight_name)
            return 0
    
        @property
        def flight_status(self):
            status = self.checking_status() #相当于调航空公司的API
            if status == 0 :
                print("flight got canceled...")
            elif status == 1 :
                print("flight is arrived...")
            elif status == 2:
                print("flight has departured already...")
            else:
                print("cannot confirm the flight status...,please check later")
    
    f = Flight("CA980")
    f.flight_status
    
    checking flight CA980 status 
    flight got canceled...
    

    这个flight_status已经是个属性了, 那我能否给它赋值呢?

    需要通过@proerty.setter装饰器再装饰一下,此时 你需要写一个新方法, 对这个flight_status进行更改

    class Flight(object):
        def __init__(self,name):
            self.flight_name = name
            self.status = self.checking_status() #相当于调航空公司的API
            
        def checking_status(self):
            print("checking flight %s status " % self.flight_name)
            return 0
    
        @property
        def flight_status(self):
            
            if self.status == 0 :
                print("flight got canceled...")
            elif self.status == 1 :
                print("flight is arrived...")
            elif self.status == 2:
                print("flight has departured already...")
            else:
                print("cannot confirm the flight status...,please check later")
        
        @flight_status.setter
        def flight_status(self,status):
            status_dic = {
                0: "canceled",
                1: "arrived",
                2: "departured"
                }
            self.status = status
            print("33[31;1mHas changed the flight status to 33[0m", status_dic.get(status))
    
        @flight_status.deleter 
        def flight_status(self):
            print("status got removed...")
            self.status = None
    
    f = Flight("CA980")
    f.flight_status
    
    
    
    checking flight CA980 status 
    flight got canceled...
    
    f.flight_status = 2 #触发@flight_status.setter
    f.flight_status
    
    
    [31;1mHas changed the flight status to [0m departured
    flight has departured already...
    
    del f.flight_status #触发@flight_status.deleter
    
    
    status got removed...
    
    f.flight_status
    
    cannot confirm the flight status...,please check later
    

    __new__

    __new__ 是一个 由 object 基类提供的 内置的静态方法,主要作用有两个:
    * 1) 在内存中为对象 分配空间
    * 2) 返回 对象的引用

    • Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__ 方法

    重写 __new__ 方法 的代码非常固定!

    • 重写 __new__ 方法 一定要 return super().__new__(cls)
    • 否则 Python 的解释器 得不到 分配了空间的 对象引用就不会调用对象的初始化方法
    • 注意:__new__ 是一个静态方法,在调用时需要 主动传递 cls 参数
    class Cat(object):
        def __new__(cls, *args, **kwargs):
            print("创建对象,分配空间")
            instance = super().__new__(cls)
            return instance
    
        def __init__(self):
            print("初始化")
    
    
    
    cat = Cat()
    
    
    创建对象,分配空间
    初始化
    
    cat1 = Cat()
    print(cat1)
    cat2 = Cat()
    print(cat2)
    
    
    print('-'*50)
    class Cat(object):
        pass
    
    
    cat1 = Cat()
    print(cat1)
    cat2 = Cat()
    print(cat2)
    
    创建对象,分配空间
    初始化
    <__main__.Cat object at 0x7f3cbc0dff60>
    创建对象,分配空间
    初始化
    <__main__.Cat object at 0x7f3cbc0dfcf8>
    --------------------------------------------------
    <__main__.Cat object at 0x7f3cbc0dff98>
    <__main__.Cat object at 0x7f3cbc0dff60>
    

    单例设计模式

    • 单例设计模式
      • 目的 —— 让 创建的对象,在系统中 只有 唯一的一个实例
      • 每一次执行 类名() 返回的对象,内存地址是相同的

    单例设计模式的应用场景

    • 音乐播放 对象
    • 回收站 对象
    • 打印机 对象

    单例流程

    1. 定义一个 **类属性**,初始值是 `None`,用于记录 **单例对象的引用**
    2. 重写 `__new__` 方法
    3. 如果 **类属性** `is None`,调用父类方法分配空间,并在类属性中记录结果
    4. 返回 **类属性** 中记录的 **对象引用**
    
    class Cat(object):
    
        # 定义类属性记录单例对象引用
        instance = None
    
        def __new__(cls, *args, **kwargs):
    
            # 1. 判断类属性是否已经被赋值
            if cls.instance is None:
                cls.instance = super().__new__(cls)
                print("创建对象,分配空间")
            # 2. 返回类属性的单例引用
            return cls.instance
        
        def __init__(self):
            print("初始化")
            
            
        
    cat1 = Cat()
    print(cat1)
    cat2 = Cat()
    print(cat2)
    
    创建对象,分配空间
    初始化
    <__main__.Cat object at 0x7f3cbc0c44e0>
    初始化
    <__main__.Cat object at 0x7f3cbc0c44e0>
    

    只执行一次初始化工作

    • 在每次使用 类名() 创建对象时,Python 的解释器都会自动调用两个方法:
      • __new__ 分配空间
      • __init__ 对象初始化
    • 在上一小节对 __new__ 方法改造之后,每次都会得到 第一次被创建对象的引用
    • 但是:初始化方法还会被再次调用

    需求

    • 初始化动作 只被 执行一次

    解决办法

    1. 定义一个类属性 init_flag 标记是否 执行过初始化动作,初始值为 False
    2. __init__ 方法中,判断 init_flag,如果为 False 就执行初始化动作
    3. 然后将 init_flag 设置为 True
    4. 这样,再次 自动 调用 __init__ 方法时,初始化动作就不会被再次执行
    class Cat(object):
        instance = None
        init_flag = False
    
        def __new__(cls, *args, **kwargs):
            if cls.instance is None:
                cls.instance = super().__new__(cls)
                print("创建对象,分配空间")
            # 2. 返回类属性的单例引用
            return cls.instance
        
        def __init__(self):
    
            if not Cat.init_flag:
                print("初始化")
                Cat.init_flag = True
    
    cat1 = Cat()
    print(cat1)
    cat2 = Cat()
    print(cat2)
    
    创建对象,分配空间
    初始化
    <__main__.Cat object at 0x7f3cbc0c4be0>
    <__main__.Cat object at 0x7f3cbc0c4be0>
    

    metaclass

    metaclass,直译为元类,简单的解释就是:

    当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

    但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

    连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

    所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

    metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。

    我们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:

    定义ListMetaclass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

    # metaclass是类的模板,所以必须从`type`类型派生:
    class ListMetaclass(type):
        def __new__(cls, name, bases, attrs):
            attrs['add'] = lambda self, value: self.append(value)
            return type.__new__(cls, name, bases, attrs)
        
        
    

    有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass

    class MyList(list, metaclass=ListMetaclass):
        pass
    
    

    当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

    __new__()方法接收到的参数依次是:

    当前准备创建的类的对象;
    
    类的名字;
    
    类继承的父类集合;
    
    类的方法集合。
    

    测试一下MyList是否可以调用add()方法:

    L = MyList()
    L.add(1)
    L.add(1)
    print(L)
    
    [1, 1]
    

    而普通的list没有add()方法:

    L2 = list()
    L2.add(1)
    
    ---------------------------------------------------------------------------
    
    AttributeError                            Traceback (most recent call last)
    
    <ipython-input-12-e9551a7fbae7> in <module>
          1 L2 = list()
    ----> 2 L2.add(1)
    
    
    AttributeError: 'list' object has no attribute 'add'
    

    动态修改有什么意义?直接在MyList定义中写上add()方法不是更简单吗?正常情况下,确实应该直接写,通过metaclass修改纯属变态。

    class MyList1(list):
        def add(self, value):
            self.append(value)  
            
            
            
    
    L = MyList1()
    L.add(1)
    L.add(1)
    print(L)
    
    
    
    [1, 1]
    

    但是,总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。

    ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。

    要编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。

    让我们来尝试编写一个ORM框架。

    编写底层模块的第一步,就是先把调用接口写出来。比如,使用者如果使用这个ORM框架,想定义一个User类来操作对应的数据库表User,我们期待他写出这样的代码:

    class User(Model):
        # 定义类的属性到列的映射:
        id = IntegerField('id')
        name = StringField('username')
        email = StringField('email')
        password = StringField('password')
    
    # 创建一个实例:
    u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
    # 保存到数据库:
    u.save()
    

    其中,父类Model和属性类型StringField、IntegerField是由ORM框架提供的,剩下的魔术方法比如save()全部由metaclass自动完成。虽然metaclass的编写会比较复杂,但ORM的使用者用起来却异常简单。

    现在,我们就按上面的接口来实现该ORM。

    首先来定义Field类,它负责保存数据库表的字段名和字段类型:

    class Field(object):
    
        def __init__(self, name, column_type):
            self.name = name
            self.column_type = column_type
    
        def __str__(self):
            return '<%s:%s>' % (self.__class__.__name__, self.name)
        
        
        
        
    

    Field的基础上,进一步定义各种类型的Field,比如StringField,IntegerField等等:

    class StringField(Field):
    
        def __init__(self, name):
            super(StringField, self).__init__(name, 'varchar(100)')
    
    class IntegerField(Field):
    
        def __init__(self, name):
            super(IntegerField, self).__init__(name, 'bigint')
            
            
            
    

    下一步,就是编写最复杂的ModelMetaclass了:

    class ModelMetaclass(type):
    
        def __new__(cls, name, bases, attrs):
            if name=='Model':
                return type.__new__(cls, name, bases, attrs)
            print('Found model: %s' % name)
            mappings = dict()
            for k, v in attrs.items():
                if isinstance(v, Field):
                    print('Found mapping: %s ==> %s' % (k, v))
                    mappings[k] = v
            for k in mappings.keys():
                attrs.pop(k)
            attrs['__mappings__'] = mappings # 保存属性和列的映射关系
            attrs['__table__'] = name # 假设表名和类名一致
            return type.__new__(cls, name, bases, attrs)
    
    

    以及基类Model:

    class Model(dict, metaclass=ModelMetaclass):
    
        def __init__(self, **kw):
            super(Model, self).__init__(**kw)
    
        def __getattr__(self, key):
            try:
                return self[key]
            except KeyError:
                raise AttributeError(r"'Model' object has no attribute '%s'" % key)
    
        def __setattr__(self, key, value):
            self[key] = value
    
        def save(self):
            fields = []
            params = []
            args = []
            for k, v in self.__mappings__.items():
                fields.append(v.name)
                params.append('?')
                args.append(getattr(self, k, None))
            sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
            print('SQL: %s' % sql)
            print('ARGS: %s' % str(args))
    

    当用户定义一个class User(Model)时,Python解释器首先在当前类User的定义中查找metaclass,如果没有找到,就继续在父类Model中查找metaclass,找到了,就使用Model中定义的metaclassModelMetaclass来创建User类,也就是说,metaclass可以隐式地继承到子类,但子类自己却感觉不到。

    ModelMetaclass中,一共做了几件事情:

    1. 排除掉对Model类的修改;

    2. 在当前类(比如User)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个__mappings__的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);

    3. 把表名保存到__table__中,这里简化为表名默认为类名。

    Model类中,就可以定义各种操作数据库的方法,比如save(),delete(),find(),update等等。

    我们实现了save()方法,把一个实例保存到数据库中。因为有表名,属性到字段的映射和属性值的集合,就可以构造出INSERT语句。

    编写代码试试:

    class User(Model):
        # 定义类的属性到列的映射:
        id = IntegerField('id')
        name = StringField('username')
        email = StringField('email')
        password = StringField('password')
    
    # 创建一个实例:
    u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
    # 保存到数据库:
    u.save()
    
    
    Found model: User
    Found mapping: id ==> <IntegerField:id>
    Found mapping: name ==> <StringField:username>
    Found mapping: email ==> <StringField:email>
    Found mapping: password ==> <StringField:password>
    SQL: insert into User (id,username,email,password) values (?,?,?,?)
    ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']
    
  • 相关阅读:
    【知了堂学习笔记】java 正则表达式
    【知了堂学习笔记】java 接口与抽象类
    【知了堂学习笔记】java web 简单的登录
    【知了堂学习笔记】java IO流归纳总结
    【知了堂学习笔记】java 自定义异常
    【知了堂学习笔记】java 方法重载与重写的归纳
    编译链接符号重定位流程简述
    项目用到了lua的哪些部分
    lua协程实现简析
    杂乱无章
  • 原文地址:https://www.cnblogs.com/feifeifeisir/p/13689744.html
Copyright © 2020-2023  润新知