xuxiaohui9216 发表于 2018-8-13 09:27:40

Python26 面向对象进阶

静态方法
  

一个简单的面向对象写法:  

  
class Dog(object):
  

  def __init__(self,name):
  self.name = name
  

  def eat(self,food):
  print ('%s is eating %s' %(self.name,food))
  

  
d = Dog('XiaoBai')
  
d.eat('包子')
  

  
执行结果:
  
XiaoBai is eating 包子
  

  

class Dog(object):  

  def __init__(self,name):
  self.name = name
  

  @staticmethod
  def eat(self,food):
  print ('%s is eating %s' %(self.name,food))
  

  
d = Dog('XiaoBai')
  
d.eat('包子')
  

  
执行结果:
  
Traceback (most recent call last):
  File "E:/Python/Day7/静态方法.py", line 13, in <module>
  d.eat('包子')
  
TypeError: eat() missing 1 required positional argument: 'food'
  
#报错少了一个food参数
  

  

class Dog(object):  

  def __init__(self,name):
  self.name = name
  

  @staticmethod   #静态方法。将下面的函数和类解除关联,也就是说下面eat()这个函数和类没什么关系了,现在只是一个单纯的函数。如果非要说有关系,只能说再调用的时候必须通过Dog.eat()调用了。
  def eat(self):
  print ('%s is eating %s' %(self.name,'abc'))    #被静态方法以后就不能再调用类的self.name了
  

  
d = Dog('XiaoBai')
  
d.eat()
  

  
执行结果:
  
Traceback (most recent call last):
  File "E:/Python/Day7/静态方法.py", line 26, in <module>
  d.eat()
  
TypeError: eat() missing 1 required positional argument: 'self'
  
少了一个位置参数self。 正常来讲会将对象传给self,但是因为静态方法,不会将对象传给eat()函数的self参数了。
  

  

class Dog(object):  

  def __init__(self,name):
  self.name = name
  

  @staticmethod
  def eat():
  print ('%s is eating %s' %('efg','abc'))
  

  
d = Dog('XiaoBai')
  
d.eat()
  

  
执行结果:
  
efg is eating abc
  
可以看到当做正常的一个普通函数来执行
  

  

让静态方法使用类的属性  
class Dog(object):
  

  def __init__(self,name):
  self.name = name
  

  @staticmethod
  def eat(self):
  print ('%s is eating %s' %(self.name,'abc'))
  

  
d = Dog('XiaoBai')
  
d.eat(d)    #将d这个对象传进函数,执行结果一样,不过这样有些多此一举,没有任何意义
  

  
执行结果:
  
XiaoBai is eating abc
  

  

  其实静态方法的作用就是让函数与类解除关系,不让其继承和访问类的内容。
  实际场景中,静态方法几乎很少有用到。

类方法
  

class Dog(object):  

  def __init__(self,name):
  self.name = name
  

  @classmethod    #类方法
  def eat(self):
  print ('%s is eating %s' %(self.name,'abc'))
  

  
d = Dog('XiaoBai')
  
d.eat()
  

  
执行结果:
  
Traceback (most recent call last):
  File "E:/Python/Day7/类方法.py", line 15, in <module>
  d.eat()
  File "E:/Python/Day7/类方法.py", line 11, in eat
  print ('%s is eating %s' %(self.name,'abc'))
  
AttributeError: type object 'Dog' has no attribute 'name'
  

  
#可以看到找不到self.name的这个name属性
  

  

class Dog(object):  

  name = 'MaoMao' #新写一个类变量(也叫类属性:在类下面,而非方法下面就是类变量)
  

  def __init__(self,name):
  self.name = name    #实例变量(也叫实例属性:self是d这个实例,所以self.name就是实例变量)
  

  @classmethod    #类方法
  def eat(self):#当前方法已经是类方法了
  print ('%s is eating %s' %(self.name,'abc'))
  

  
d = Dog('XiaoBai')
  
d.eat()
  

  
执行结果:
  
MaoMao is eating abc
  
可以看到这个name用到的是类变量的'MaoMao',而不是实例化时传的'XiaoBai'
  

  

  结论:类方法只能访问类变量,不能访问实例变量。
  使用场景:当变量不允许被实例化时修改时可以使用,比如你的亲生父亲是张三,而实例化的时候想给你改成李四,这样是无法被改动的,调用的依然是张三(已经被写死了)。

属性方法
  

class Dog(object):  

  name = 'MaoMao'
  

  def __init__(self,name):
  self.name = name
  

  @property    #属性方法
  def eat(self):
  print ('%s is eating %s' %(self.name,'abc'))
  

  
d = Dog('XiaoBai')
  
d.eat()
  

  
执行结果:
  
Traceback (most recent call last):
  File "E:/Python/Day7/属性方法.py", line 16, in <module>
  d.eat()
  
TypeError: 'NoneType' object is not callable
  
#提示:空类型不可调用
  

  

class Dog(object):  

  name = 'MaoMao'
  

  def __init__(self,name):
  self.name = name
  

  @property    #属性方法
  def eat(self):
  print ('%s is eating %s' %(self.name,'abc'))
  

  
d = Dog('XiaoBai')
  
d.eat   #将括号去掉,此时就不是调用一个方法了,而是调用一个属性
  

  
执行结果:
  
XiaoBai is eating abc
  

  

  属性方法:就是将一个方法变成一个静态属性
  

class Dog(object):  

  name = 'MaoMao'
  

  def __init__(self,name):
  self.name = name
  

  @property    #属性方法
  def eat(self,food):
  print ('%s is eating %s' %(self.name,food))
  

  
d = Dog('XiaoBai')
  
d.eat
  

  
执行结果:
  
Traceback (most recent call last):
  File "E:/Python/Day7/属性方法.py", line 16, in <module>
  d.eat
  
TypeError: eat() missing 1 required positional argument: 'food'
  
#少了一个food参数;这说明了如果将方法变成属性的话,就没办法传参数进去了
  

  

class Dog(object):  

  name = 'MaoMao'
  

  def __init__(self,name):
  self.name = name
  

  @property    #属性方法
  def eat(self):
  print ('%s is eating %s' %(self.name,'abc'))
  

  @eat.setter #通过该方法给eat属性传参数
  def eat(self,food):
  print ('set to food:',food)
  

  
d = Dog('XiaoBai')
  
d.eat
  
d.eat = 'baozi' #对eat这个属性赋值;这里触发的是@eat.setter下面的def eat,将'baozi'传给food
  

  
执行结果:
  
XiaoBai is eating abc
  
set to food: baozi
  

  

class Dog(object):  

  name = 'MaoMao'
  

  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#将food这个参数赋值给self.__food
  

  
d = Dog('XiaoBai')
  
d.eat   #这里关联的是@roperty下的def eat
  
d.eat = 'baozi' #这里关联的是@eat.setter下的 def eat
  
d.eat   #这里关联的是@roperty下的def eat,不过此时__food已经被赋值为'baozi'
  

  
执行结果:
  
XiaoBai is eating None
  
set to food: baozi
  
XiaoBai is eating baozi
  

  

删除属性方法(1)  
class Dog(object):
  

  name = 'MaoMao'
  

  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
  

  
d = Dog('XiaoBai')
  
d.eat
  
d.eat = 'baozi'
  
d.eat
  

  
del d.eat   #删除属性方法
  

  
执行结果:
  
XiaoBai is eating None
  
Traceback (most recent call last):
  
set to food: baozi
  File "E:/Python/Day7/属性方法.py", line 25, in <module>
  
XiaoBai is eating baozi
  del d.eat
  
AttributeError: can't delete attribute
  

  
#可以看到属性方法此时是无法删除的
  

  

删除属性方法(2)  

  
class Dog(object):
  

  name = 'MaoMao'
  

  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#将food这个参数赋值给self.__food
  

  @eat.deleter
  def eat(self):
  del self.__food
  print ('已删除!')
  

  
d = Dog('XiaoBai')
  
d.eat
  
d.eat = 'baozi'
  
d.eat
  

  
del d.eat   #删除属性方法
  

  
d.eat   #此时__food已经被删除,在执行该代码的话应该会报错
  

  
执行结果:
  
Traceback (most recent call last):
  
XiaoBai is eating None
  
set to food: baozi
  File "E:/Python/Day7/属性方法.py", line 32, in <module>
  
XiaoBai is eating baozi
  d.eat
  
已删除!
  File "E:/Python/Day7/属性方法.py", line 13, in eat
  print ('%s is eating %s' %(self.name,self.__food))
  
AttributeError: 'Dog' object has no attribute '_Dog__food'
  

  

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


[*]  连接航空公司API查询

[*]  对查询结果进行解析 (对提取过来的数据进行解析,这个数据可能是json,可能是xml等)

[*]返回结果给你的用户 (需要将json、xml等格式的数据转成客户可读、可视化的结果)
  因此这个status属性的值是一系列动作后才得到的结果,所以你每次调用status属性时,其实它都要经过一系列动态的动作才返回你结果,但这些动作过程不需要用户关心,用户只需要调用这个属性就可以,明白 了么?
  

航班查询:  
class Flight(object):   #实例化的类
  def __init__(self,name):
  self.flight_name = name   #实例化时传航班名字
  

  def checking_status(self):#方法:检查航班状态
  print("checking flight %s status " % self.flight_name)
  return1   #这里表示航班的状态值
  

  @property
  def flight_status(self):    #方法:检查航班状态,不过这里是用于提供给客户数据的
  status = self.checking_status() #先去检查状态,这里相当于调用航空公司的API
  if status == 0 :    #航空公司只返回0、1等这样的数据
  print("flight got canceled...") #如果等于0,表示飞机取消了(这里是我们给客户反馈的可读、可视化数据)
  elif status == 1 :
  print("flight is arrived...")   #如果等于1,表示飞机到达了
  elif status == 2:
  print("flight has departured already...")   #如果等于2,表示飞机离开了
  else:
  print("cannot confirm the flight status...,please check later")#表示未知,无法确认,需要等一会在查询
  

  @flight_status.setter
  def flight_status(self,status):
  print ('flight %s has changed status to %s' %(self.flight_name,status))
  

  
f = Flight("CA980") #实例化类,并传航班名称叫'CA980'
  
f.flight_status #调用状态,对于客户来说就这一步,调用数据,而不知道实际上面还有很多动作(代码)
  

  
#cool , 那现在我只能查询航班状态, 既然这个flight_status已经是个属性了, 那我能否给它赋值呢?试试吧
  

  
f.flight_status = 2
  

  
执行结果:
  
checking flight CA980 status
  
flight is arrived...
  
flight CA980 has changed status to 2
  

  

  属性方法主要就是用来隐藏实现细节,@property下def flight_status的细节对于客户来说是都看不到的
  输出, 说不能更改这个属性,我擦。。。。,怎么办怎么办。。。
  

  
checking flight CA980 status
  
flight is arrived...
  
Traceback (most recent call last):
  File "/Users/jieli/PycharmProjects/python基础/自动化day7面向对象高级/属性方法.py", line 58, in <module>
  f.flight_status =2
  
AttributeError: can't set attribute
  

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

class Flight(object):  def __init__(self,name):
  self.flight_name = name
  

  def checking_status(self):
  print("checking flight %s status " % self.flight_name)
  return1
  

  @property
  def flight_status(self):
  status = self.checking_status()
  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")
  

  @flight_status.setter #修改
  def flight_status(self,status):
  status_dic = {
  
: "canceled",
  
:"arrived",
  
: "departured"
  }
  print("\033[31;1mHas changed the flight status to \033[0m",status_dic.get(status) )
  

  @flight_status.deleter#删除
  def flight_status(self):
  print("status got removed...")
  

  
f = Flight("CA980")
  
f.flight_status
  
f.flight_status =2 #触发@flight_status.setter
  
del f.flight_status #触发@flight_status.deleter
  

类的特殊成员方法
  

__doc__:显示注释信息  

  
class Test:
  '''描述信息,这里用来描述类的信息'''
  

  def func(self):
  pass
  
print (Test.__doc__)
  

  
执行结果:
  
描述信息,这里用来描述类的信息#显示类中的注释信息
  

  

__module__:表示当前操作的对象在哪个模块  
__class__ :表示当前操作的对象的类是什么
  

  
-----------------------------------在A2模块中的内容
  
class C:
  

  def __init__(self):
  self.name = 'wupeiqi'
  
-----------------------------------在A2模块中的内容
  

  
-----------------------------------在A1模块中的内容
  
from A2 import C
  

  
obj = C()
  
print (obj.__module__)
  
print (obj.__class__)
  
-----------------------------------在A1模块中的内容
  

  
执行结果:
  
A2#这个C是从A2模块导入的
  
<class 'A2.C'>
  
#在A1模块中print可以看到相应的结果
  

  

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

  
class Foo:
  def __init__(self):
  print ('abc')
  

  
obj = Foo()# 执行 __init__
  
obj()# 执行 __call__
  

  
执行结果:
  
abc
  
Traceback (most recent call last):
  File "E:/python/代码练习/A1.py", line 20, in <module>
  obj()# 执行 __call__
  
TypeError: 'Foo' object is not callable
  
#不可以直接执行obj()
  

  
class Foo:
  def __init__(self):
  print ('abc')
  

  def __call__(self, *args, **kwargs):
  print ('running call')
  

  
obj = Foo()# 执行 __init__
  
obj()# 执行 __call__
  

  
执行结果:
  
abc
  
running call
  

  
class Foo:
  def __init__(self):
  print ('abc')
  

  def __call__(self, *args, **kwargs):
  print ('running call',args,kwargs)
  

  
obj = Foo()# 执行 __init__
  
obj(1,2,3,name=123)# 执行 __call__
  

  
执行结果:
  
abc
  
running call (1, 2, 3) {'name': 123}
  
#给__call__方法传参数
  

  
class Foo:
  def __init__(self):
  print ('abc')
  

  def __call__(self, *args, **kwargs):
  print ('running call',args,kwargs)
  

  
Foo()()
  
执行结果:
  
abc
  
running call () {}
  

  

class Foo:  '''描述信息'''
  abc = 123
  def __init__(self,name):
  print ('abc')
  

  def __call__(self, *args, **kwargs):
  print ('running call',args,kwargs)
  

  
print (Foo.__dict__)    #通过类直接调用__dict__,打印类里的所有属性,不包括实力属性
  

  
执行结果:
  
{'__module__': '__main__', '__doc__': '描述信息', 'abc': 123, '__init__': <function Foo.__init__ at 0x0000015DED8F1B70>, '__call__': <function Foo.__call__ at 0x0000015DED8F1BF8>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>}
  
abc
  

  
d = Foo('haha') #打印所有实例属性,不包括类属性
  
# print (d.__dict__)
  

  
执行结果:
  
abc
  
{}
  

  

__str__  

  
class Foo:
  '''描述信息'''
  abc = 123
  def __init__(self,name):
  self.name = name
  

  def __call__(self, *args, **kwargs):
  print ('running call',args,kwargs)
  

  
d = Foo('haha')
  
print (d)
  

  
执行结果:
  
<__main__.Foo object at 0x000001A5ED057C18>
  
#打印的是对象的内存地址
  

  
class Foo:
  '''描述信息'''
  abc = 123
  def __init__(self,name):
  self.name = name
  

  def __call__(self, *args, **kwargs):
  print ('running call',args,kwargs)
  

  def __str__(self):
  return '<obj:%s>' %self.name    #将字符串return给类
  

  
d = Foo('haha')
  
print (d)
  

  
执行结果:
  
<obj:haha>
  

  
#__str__以后会经常用到
  

  

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

  
class Foo(object):
  def __getitem__(self, key):
  print('__getitem__', key)
  

  def __setitem__(self, key, value):
  print('__setitem__', key, value)
  

  def __delitem__(self, key):
  print('__delitem__', key)   #虽然已触发,但这里并没有删除,可以自己执行删除 del ......
  

  
obj = Foo()
  
result = obj['k1']      # 自动触发执行 __getitem__
  
obj['k2'] = 'alex'   # 自动触发执行 __setitem__
  
del obj['k1']   #自动触发执行__delitem__
  

  
#注意只是触发而已,实际的动作还是要看方法下面的动作,比如当前的动作只是print
  

  
执行结果:
  
__getitem__ k1
  
__setitem__ k2 alex
  
__delitem__ k1
  

  

类的起源与metaclass
  

__new__ \ __metaclass__  

  
class Foo(object):
  

  def __init__(self,name):
  self.name = name
  

  
f = Foo("alex")
  

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

  
执行结果:
  
<class '__main__.Foo'>#f这个对象来自Foo这个类
  
<class 'type'>#Foo这个对象来自type这个类
  

  

  上述代码中,obj 是通过 Foo 类实例化的对象,其实,不仅 obj 是一个对象,Foo类本身也是一个对象,因为在Python中一切事物都是对象。
  如果按照一切事物都是对象的理论:obj对象是通过执行Foo类的构造方法创建,那么Foo类对象应该也是通过执行某个类的 构造方法 创建。
  所以,f对象是Foo类的一个实例,Foo类对象是 type 类的一个实例,即:Foo类对象 是通过type类的构造方法创建。
  那么,创建类就可以有两种方式:
  

普通方式:  
class Foo(object):
  

  def func(self):
  print ('hello zhangsan')
  

  
f = Foo()
  

  

特殊方式:  

  
def func(self):
  print ('hello zhangsan')
  

  
Foo = type('Foo',(), {'talk': func})#将def func与该字典的value func关联
  
#type第一个参数:类名
  
#type第二个参数:当前类的基类
  
#type第三个参数:类的成员
  

  
#Foo = type('Foo'   这里表示Foo是基于type的,也就是Foo这个类的起源是type
  

  
print (type(Foo))
  

  
f = Foo()
  
f.talk()
  

  
#f是Foo的对象,Foo是f的类;
  
#Foo是type的对象,type是Foo的类
  
#type是类(Foo)的类
  

  

特殊方式:  

  
def func(self):
  print ('hello %s'%self.name)
  

  
def __init__ (self,name,age):
  self.name = name
  self.age = age
  

  
Foo = type('Foo',(object,), {'talk': func,'__init__':__init__})
  
#用object定义为新式类,object那里要加一个逗号,因为它是一个元组,如果不加的话,括号和object就相当于一个值了。
  
#__init__关联构造函数
  
print (type(Foo))
  

  
f = Foo('zhangsan',22)#实例化
  

  
f.talk()
  

  

class MyType(type):  def __init__(self,*args,**kwargs):
  

  print("Mytype __init__",*args,**kwargs)
  

  def __call__(self, *args, **kwargs):
  print("Mytype __call__", *args, **kwargs)
  obj = self.__new__(self)
  print("obj ",obj,*args, **kwargs)
  print(self)
  self.__init__(obj,*args, **kwargs)
  return obj
  

  def __new__(cls, *args, **kwargs):
  print("Mytype __new__",*args,**kwargs)
  return type.__new__(cls, *args, **kwargs)
  

  
# print('here...')
  
class Foo(object):
  #__metaclass__ = MyType
  

  def __init__(self,name):
  self.name = name
  

  print("Foo __init__")
  

  def __new__(cls, *args, **kwargs):#__new__和__init__一样,是类自带的方法,不过__new__会比__init__先执行
  print("Foo __new__",cls, *args, **kwargs)
  #return object.__new__(cls)
  

  
f = Foo("Alex")
  
# print("f",f)
  
# print("fname",f.name)
  

  
执行结果:
  
Foo __new__ <class '__main__.Foo'> Alex #可以看到__new__比__init__先执行了。
  
Foo __init__
  

  

class MyType(type):  def __init__(self,*args,**kwargs):
  

  print("Mytype __init__",*args,**kwargs)
  

  def __call__(self, *args, **kwargs):
  print("Mytype __call__", *args, **kwargs)
  obj = self.__new__(self)
  print("obj ",obj,*args, **kwargs)
  print(self)
  self.__init__(obj,*args, **kwargs)
  return obj
  

  def __new__(cls, *args, **kwargs):
  print("Mytype __new__",*args,**kwargs)
  return type.__new__(cls, *args, **kwargs)
  

  
# print('here...')
  
class Foo(object):
  #__metaclass__ = MyType
  

  def __init__(self,name):
  self.name = name
  

  print("Foo __init__")
  

  def __new__(cls, *args, **kwargs):
  print("Foo __new__",cls, *args, **kwargs)
  # return object.__new__(cls)    #注释掉
  

  
f = Foo("Alex") #这里已经创建了实例,只不过会被__new__重构
  
# print("f",f)
  
# print("fname",f.name)
  

  
执行结果:
  
Foo __new__ <class '__main__.Foo'> Alex
  
#可以看到只执行了__new__方法,而__init__没有执行,这是因为通过__new__来实例化,在调用了__init__
  

  
这个__new__是默认存在的,在这里写相当于重构了,一般没有去重构__new__的,这里改写只不过是为了了解__new__的存在和作用。
  

  

class MyType(type):  def __init__(self,*args,**kwargs):
  

  print("Mytype __init__",*args,**kwargs)
  

  def __call__(self, *args, **kwargs):
  print("Mytype __call__", *args, **kwargs)
  obj = self.__new__(self)
  print("obj ",obj,*args, **kwargs)
  print(self)
  self.__init__(obj,*args, **kwargs)
  return obj
  

  def __new__(cls, *args, **kwargs):
  print("Mytype __new__",*args,**kwargs)
  return type.__new__(cls, *args, **kwargs)   #去继承父类的__new__方法
  

  
# print('here...')
  
class Foo(object):
  # __metaclass__ = MyType
  

  def __init__(self,name):
  self.name = name
  

  print("Foo __init__")
  

  def __new__(cls, *args, **kwargs):
  print("Foo __new__",cls, *args, **kwargs)
  return object.__new__(cls)
  

  
f = Foo("Alex")
  

  


  metaclass 详解文章:http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python 得票最高那个答案写的非常好
页: [1]
查看完整版本: Python26 面向对象进阶