面向对象高级特性

静态方法

通过@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

 

更多定制类用法

 

 

  

  

 

 

 

  

posted @ 2017-06-18 12:51  zj-luxj  阅读(158)  评论(0编辑  收藏  举报