• 面向对象高级特性


    静态方法

    通过@staticmethod装饰器即可把其装饰的方法变为一个静态方法,什么是静态方法呢?其实不难理解,普通的方法,可以在实例化后直接调用,并且在方法里可以通过self.调用实例变量或类变量,但静态方法是不可以访问实例变量或类变量的,一个不能访问实例变量和类变量的方法,其实相当于跟类本身已经没什么关系了,它与类唯一的关联就是需要通过类名来调用这个方法

    class Dog(object):
    
        def __init__(self,name):
            self.name = name
    
        @staticmethod  #把eat变为静态方法
        def eat(self):
            print("{} is eating {}".format(self.name,'bone'))
    
    d = Dog('wangcai')
    d.eat()
    结果会报错
    Traceback (most recent call last):
      File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 103, in <module>
        d.eat()
    TypeError: eat() missing 1 required positional argument: 'self'
    

      程序中调用eat()方法会报错,报错信息为缺少一个位置参数。把eat变为静态方法实际上跟类没什么关系,把这个方法和类的关联截断了,其实就是一个函数,只是需要通过类调用。那我们要如何调用呢?其实很简单,就是按照函数的传参把参数传进去

    class Dog(object):
    
        def __init__(self,name):
            self.name = name
    
        @staticmethod 
        def eat(self):
            print("{} is eating {}".format(self.name,'bone'))
    
    d = Dog('wangcai')
    d.eat(d)
    
    结果:
    wangcai is eating bone
    

     

    class Dog(object):
    
        def __init__(self,name):
            self.name = name
    
        @staticmethod
        def eat():
            print("{} is eating {}".format('wangcai','bone'))
    
    d = Dog('wangcai') 
    d.eat()  #实例化后再调用
    
    结果:
    wangcai is eating bone
    --------------------------------------------------------
    
    class Dog(object):
    
        def __init__(self,name):
            self.name = name
    
        @staticmethod
        def eat():
            print("{} is eating {}".format('wangcai','bone'))
    
    Dog.eat()  #实例化后再调用没什么意义。不实例化,直接调用
    结果: wangcai is eating bone

    类方法 

    类方法通过@classmethod装饰器实现,类方法和普通方法的区别是, 类方法只能访问类变量,不能访问实例变量,为什么会这样呢?继续往下看

    class Dog(object):
    
        def __init__(self):
            self.name = 'lx'
    
        @classmethod
        def eat(cls):
            print("{} is eating {}".format(cls.name,'bone'))
    d = Dog("aaaaa")
    d.eat()
    结果会报错: AttributeError: type object 'Dog' has no attribute 'name'
    class Dog(object):
        name = 'bbbb'
        def __init__(self):
            # self.name = 'lx'
            pass
    
        @classmethod
        def eat(cls):
            print("{} is eating {}".format(cls.name,'bone'))
    d = Dog("aaaaa")
    d.eat()
    结果: bbbb is eating bone

    类方法和静态方法的区别:两者都可以通过类来调用,类方法中的cls参数就是类本身,静态方法默认没用参数,相当于函数,只是通过类来调用,需要什么参数再传进去

    class Dog(object):
        name = 'bbbb'
        def __init__(self):
            # self.name = 'lx'
            pass
        @staticmethod
        def run(cls):
            print("{} running....".format(cls.name))
    
        @classmethod
        def eat(cls):
            print(cls)
            print("{} is eating {}".format(cls.name,'bone'))
    
    Dog.run(Dog)
    Dog.eat()
    
    结果:
    bbbb running....
    <class '__main__.Dog'>
    bbbb is eating bone
    

      

    属性方法

    属性方法的作用就是通过@property把一个方法变成一个静态属性

    class Dog(object):
        #name = 'bbbb'
        def __init__(self,name):
            self.name = name
    
        @property
        def eat(self):
            print("{} is eating {}".format(self.name,'bone'))
    
    d = Dog("aaaaa")
    d.eat()
    结果会报错:
    TypeError: 'NoneType' object is not callable
    

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

    class Dog(object):
        #name = 'bbbb'
        def __init__(self,name):
            self.name = name
    
        @property
        def eat(self):
            print("{} is eating {}".format(self.name,'bone'))
    
    d = Dog("aaaaa")
    d.eat
    

    既然是属性,那就可以赋值,那么如何对属性赋值呢?可以使用@setter  

    class Dog(object):
        #name = 'bbbb'
        def __init__(self,name):
            self.name = name
    
        @property
        def eat(self):
            print("{} is eating {}".format(self.name,'bone'))
    
        @eat.setter
        def eat(self,name):
            self.name = name
    d = Dog("aaaaa")
    d.eat = 'ccccc' #实际转化为d.eat('ccccc')
    d.eat            #实际转化为d.eat()
    
    结果:
    ccccc is eating bone
    

    既然是属性那应该可以删除。如果直接使用del d.eat删除,发现会报错:AttributeError: can't delete attribute.需使用@deleter方法

    class Dog(object):
        #name = 'bbbb'
        def __init__(self,name):
            self.name = name
    
        @property
        def eat(self):
            print("{} is eating {}".format(self.name,'bone'))
    
        @eat.setter
        def eat(self,name):
            self.name = name
        @eat.deleter
        def eat(self):
            del self.name
            print("删完了")
    d = Dog("aaaaa")
    d.eat = 'ccccc' #实际转化为d.eat('ccccc')
    d.eat            #实际转化为d.eat()
    del d.eat        #实际转化为d.eat()
    d.eat
    
    结果:
    ccccc is eating bone
    删完了
    

      

    类的特殊成员方法

    •  __doc__  表示类的描述信息

    class Dog(object):
        """
        描述狗这个对象
        """
        def __init__(self,name):
            self.name = name
    
    print(Dog.__doc__)
    结果:
    
        描述狗这个对象 
    •  __module__ 和  __class__

    __module__ 表示当前操作的对象所属类在哪个模块

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

    class Dog(object):
        """
        描述狗这个对象
        """
        def __init__(self,name):
            self.name = name
    
    from test1 import Dog
    
    d = Dog('bbbb')
    print(d.__module__)
    print(d.__class__)
    
    结果:
    test1
    <class 'test1.Dog'>
    
    •  __init__ 构造方法,通过类创建对象时,自动触发执行。

    • __del__

     析构方法,当对象在内存中被释放时,自动触发执行。

    注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的

      

    •   __call__ 对象后面加括号,触发执行。

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

    class Dog(object):
        """
        描述狗这个对象
        """
        def __init__(self,name):
            self.name = name
        def __call__(self, *args, **kwargs):
            print("__call__")
    
    d = Dog('aaaa')
    d()
    
    结果:__call__
    
    • __dict__ 查看类或对象中的所有成员 

    class Role(object):
        school = '树人'
        def __init__(self,name):
            self.name = name
            self.age = 27
    
    
    d = Role('lxj')
    print("实例化对象成员字典:",d.__dict__)
    print("类的成员字典:",Role.__dict__)
    
    结果:
    实例化对象成员字典: {'name': 'lxj', 'age': 27}
    类的成员字典: {'__dict__': <attribute '__dict__' of 'Role' objects>, '__init__': <function Role.__init__ at 0x006B0270>, '__weakref__': <attribute '__weakref__' of 'Role' objects>, '__doc__': None, 'school': '树人', '__module__': '__main__'}
    
    • __str__ 如果一个类中定义了__str__方法,那么在打印 对象 时,默认输出该方法的返回值,不影响实例的调用。

    class Role(object):
        
        def __init__(self,name):
            self.name = name
            self.age = 27
        def __str__(self):
            return "str..."
    
    
    d = Role('lxj')
    print(d)
    
    结果:
    str...
    
    • __getitem__、__setitem__、__delitem__

    用于索引操作,如字典。以上分别表示获取、设置、删除数据

    class Foo(object):
        def __init__(self):
            self.data = { }
        def __getitem__(self, key):
            print('__getitem__', key)
            return self.data.get(key)
        def __setitem__(self, key, value):
            print('__setitem__', key, value)
            self.data[key] = value
    
        def __delitem__(self, key):
            print('__delitem__', key)
            del self.data[key]
    
    obj = Foo()
    
    
    obj['k2'] = 'alex'  # 自动触发执行 __setitem__
    
    result = obj['k2'] #自动触发执行 __getitem__
    print("result is :{}".format(result))
    
    del obj['k2']               #自动触发执行 __delitem__
    
    结果:
    __setitem__ k2 alex
    __getitem__ k2
    result is :alex
    __delitem__ k2
    
    • __slots__

    class Student(object):
        pass
    
    s = Student()
    s.name = "Michael"   #给实例绑定属性
    print(s.name)
    结果:
    Michael
    
    from types import MethodType
    
    def set_age(self,age):
        self.age = age
    
    class Student(object):
        pass
    
    s = Student()
    s.set_age = MethodType(set_age,s)   #给实例绑定一个方法,只对该实例有用
    s.set_age(27)    #调用方法
    print(s.age)
    结果:
    27
    
    #给所有实例绑定方法可以用Student.set_age = set_age
    
    #但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。
    #为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实#例能添加的属性:
    class Student(object):
        __slots__ = ('name','age')
        pass
    
    s = Student()
    s.name = 'lxj'
    s.age = 27
    s.score = 99
    结果:
    Traceback (most recent call last):
      File "D:/python_work/elective_system/test.py", line 39, in <module>
        s.score = 99
    AttributeError: 'Student' object has no attribute 'score'
    
    
    #由于'score'没有被放到__slots__中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误。
    
    #使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
    
    • __repr__

    class Role(object):
        def __init__(self, name,age):
            self.name = name
            self.age = age
    
        def __str__(self):
            return "({0.name},{0.age})".format(self)
    
        def __repr__(self):
            return 'Role({0.name},{0.age})'.format(self)
    
    
    d = Role('lxj',21)
    print("d is {!r}".format(d))   #显示repr
    print("d is {!s}".format(d))   #显示str
    结果:
    d is Role(lxj,21)
    d is (lxj,21)
    
    #与__str的区别,__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。
    
    #通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:
    class Role(object):
        def __init__(self, name,age):
            self.name = name
            self.age = age
    
        def __str__(self):
            return "({0.name},{0.age})".format(self)
    
        __repr__ = __str__
    
    
    d = Role('lxj',21)
    print("d is {!r}".format(d))   #显示repr
    print("d is {!s}".format(d))   #显示str
    结果:
    d is (lxj,21)
    d is (lxj,21)
    
    • __getattr__

    #首先来看个例子,当我们调用不存在的属性时,会报错
    
    class Role(object):
        def __init__(self, name):
            self.name = name
    
    s = Role("lxj")
    print(s.name)
    print(s.age)
    
    结果:
    lxj
    Traceback (most recent call last):
      File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 150, in <module>
        print(s.age)
    AttributeError: 'Role' object has no attribute 'age'
    
    #那如何避免这个错误呢?1、可以加上一个score属性外;2、Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。修改如下:
    
    class Role(object):
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, item):
            if item == 'age':
                return 23
    
    s = Role("lxj")
    print(s.name)
    print(s.age)
    
    结果:
    lxj
    23
    
    #当调用不存在的属性时,比如age,Python解释器会试图调用__getattr__(self, 'score')来尝试获得属性,这样,我们就有机会返回score的值,也可以返回函数
    
    #此时还有个问题,当我们调用其他不存在的属性,不会报错了,而是打印None
    如:print(s.asd) -> None,我们有必要对上面的程序再优化下,当调用不存在的属性,继续报错
    class Role(object):
        def __init__(self, name):
            self.name = name
    
        def __getattr__(self, item):
            if item == 'age':
                return 23
            raise AttributeError("{self.__class__.__name__} object has no attribute {item}".format(self = self,item = item))
    
    s = Role("lxj")
    print(s.name)
    print(s.age)
    
    print(s.asd)
    
    结果:
    Traceback (most recent call last):
    lxj
      File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 157, in <module>
    23
        print(s.asd)
      File "D:/lxj/123/py3_training/atm/shopping_mall/test1.py", line 151, in __getattr__
        raise AttributeError("{self.__class__.__name__} object has no attribute {item}".format(self = self,item = item))
    AttributeError: Role object has no attribute asd
    

      

    这实际上可以把一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段。

    这种完全动态调用的特性有什么实际作用呢?作用就是,可以针对完全动态的情况作调用。  

    • __new__ __metaclass__

    class Foo(object):
     
     
        def __init__(self,name):
            self.name = name
     
     
    f = Foo("lxj")
    

    上述代码中,f是通过 Foo 类实例化的对象,其实,不仅f是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象

    如果按照一切事物都是对象的理论:f对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的构造方法创建。

    print type(f) # 输出:<class '__main__.Foo'>     表示,obj 对象由Foo类创建
    print type(Foo) # 输出:<type 'type'>              表示,Foo类对象由 type 类创建
    

    所以,f对象是Foo类的一个实例Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。

    类创建方式

    1、普通方式

    class Foo(object):
        def func(self):
            print("hello lxj")
    

    2、特殊方式 

    def func(self):
        print("hello lxj")
    
    Foo = type('Foo',(object,),{'func':func})
    #type第一个参数为类名
    #type第二个参数为当前类的基类,为元组形式
    #type第三个参数为类的成员,为字典形式
    
     1 def func(self):
     2     print("hello {}".format(self.name))
     3 
     4 def __init__(self,name,age):
     5     self.name = name
     6     self.age = age
     7 
     8 Foo = type('Foo',(object,),{'func':func,'__init__':__init__})
     9 
    10 
    11 a = Foo('lxj',27)
    12 a.func()
    添加构造方法

    类 是由 type 类实例化产生

    那么问题来了,类默认是由 type 类实例化产生,type类中如何实现的创建类?类又是如何创建对象?

    答:类中有一个属性 __metaclass__(元类),其用来表示该类由 谁 来实例化创建,所以,我们可以为 __metaclass__ 设置一个type类的派生类,从而查看 类 创建的过程。

    class Foo(object):
        #__metaclass__ = MyType
    
        def __init__(self,name):
            self.name = name
            print("Foo __init__")
    
        def __new__(cls, *args, **kwargs):
            print("Foo __new__")
            return object.__new__(cls)   #继承父亲的__new__方法
    
    Foo('lxj')
    结果:
    Foo __new__
    Foo __init__
    
    #此时我们可以得出结论__new__在__init__之前运行,现在我们注释掉return object.__new__(cls)
    
    def __new__(cls, *args, **kwargs):
        print("Foo __new__")
        #return object.__new__(cls)
    a = Foo('lxj')
    print(a)
    结果:
    Foo __new__
    None
    #此时我们发现根本就没有实例化,我们可以得出结论new是用来创建实例的
    
    class MyType(type):
        def __init__(self,what,bases=None,dict=None):
            print("MyType __init__")
            super().__init__(what,bases,dict)
    
        def __call__(self, *args, **kwargs):
            print("MyType __call__")
            obj = self.__new__(self,*args,**kwargs)
    
            self.__init__(obj,*args,**kwargs)
    
    
    
    class Foo(object):
        __metaclass__ = MyType
    
        def __init__(self,name):
            self.name = name
            print("Foo __init__")
    
        def __new__(cls, *args, **kwargs):
            print("Foo __new__")
            return object.__new__(cls)
    
    a = Foo('lxj')
    
    #在python3上应该这么写class Foo(object,metaclass=MyType),运行结果与python2一致
    MyType __init__
    MyType __call__
    Foo __new__
    Foo __init__
    

      

     关于元类的使用:https://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python?answertab=votes#tab-top

    更多定制类用法

      

      

      

  • 相关阅读:
    bootstrap fileinput 无法显示中文bug
    js防止回车(enter)键提交表单及javascript中event.keycode
    php 生成唯一随机码
    thinksns 分页数据
    详解PHP处理密码的几种方式
    windows7 在cmd中执行php脚本
    php 无限级分类 递归+sort排序 和 非递归
    CentOS 创建SVN 服务器,并且自动同步到WEB 目录
    微擎笔记
    laravel php框架 知识点及注意问题
  • 原文地址:https://www.cnblogs.com/zj-luxj/p/7044040.html
Copyright © 2020-2023  润新知