Python之面向对象进阶

                            面向对象进阶                                   

 阅读目录 

  • issubclass和isinstance
  • 反射 
  1. setattr
  2. getattr
  3. hasattr
  4. delattr
  • __str__和__repr__
  • item系列
  1. __getitem__
  2. __setitem__
  3. __delitem__
  • __del__
  • __new__
  • __call__
  • __len__
  • __hash__
  • __eq__

 

isinstance和issubclass                                                                              

isinstance(obj,cls)检查是否obj是否是类 cls 的对象。
issubclass(sub, super)检查sub类是否是 super 类的派生类。 

class Foo(object):pass
  
obj = Foo()
  
isinstance(obj, Foo)
isinstance
class Foo(object):pass
class Bar(Foo):pass
 
print(issubclass(Bar, Foo))
#返回True
issubclass

反射                                                                                                             

1.什么是反射

  反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

2.python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)。

  四个可以实现自省的函数,通过反射:

  1. getattr(对象名,获取对象属性/普通方法)
  2. getattr(类名,获取静态属性/类方法/静态方法)
  3. 普通方法 self
  4. 静态方法 @staticmethod
  5. 类方法 @classmethod
  6. 属性方法 @property

  下列方法适用于类和对象(一切皆对象,类本身也是一个对象)。

def hasattr(对象名/类名,属性/静态方法/普通方法): # real signature unknown
    """
    Return whether the object has an attribute with the given name.
    
    This is done by calling getattr(obj, name) and catching AttributeError.
    """
    pass
hasattr
def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value
    
    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.
    """
    pass
getattr
def setattr(x, y, v): # real signature unknown; restored from __doc__
    """
    Sets the named attribute on the given object to the specified value.
    
    setattr(x, 'y', v) is equivalent to ``x.y = v''
    """
    pass
setattr
def delattr(x, y): # real signature unknown; restored from __doc__
    """
    Deletes the named attribute from the given object.
    
    delattr(x, 'y') is equivalent to ``del x.y''
    """
    pass
delattr
四种方法的使用
class A:
    def func(self):
        print('in func')
    
a=A()
a.name='eric'
#反射对象的属性
ret=getattr(a,'name')#通过变量的字符串形式取到值
print(ret)
#反射对象的方法
ret=getattr(a,'func')()#通过getattr拿到函数的地址,通过加()的方式进行函数调用
反射对象中的属性和方法
class A:
    price=20
    def func(self):
        print('in func')
        
ret=getattr(A,'price')
print(ret)
反射类的属性
class A:
    @classmethod#加上这句才算是将普通方法变为类方法
    def func(cls):#注意此处的参数,当存在上面类方法的装饰器,此处的参数一般为cls,否则普通方法为self
        print('in func')
if hasattr(A,'func'):#相当于有才执行,没有不执行
    ret=getattr(A,'func')
    ret()
反射类的方法#classmethod,staticmethod
import my
#反射模块的属性
print(getattr(my,'day'))
   
#反射模块的方法
getattr(my,'func')()

##模块中的程序
##day=20
##def func():
##    print('模块中的函数')
模块
import sys
print(sys.modules['__main__'])
def eerriicc():
    print('eric')
money=100
getattr(sys.modules['__main__'],'eerriicc')()#这样就实现了自己模块中的方法的调用
print(sys.modules['__main__'].money)
print(getattr(sys.modules['__main__'],'money'))#这样就实现了自己模块中属性的调用
内置模块也可用--反射自己模块中的变量

注意:如果还想通过程序调用此模块中的属性或方法,就必须将'__main__'改为一个变量。即:print(getattr(sys.modules[__name__],变量名))

例如:

import sys
def s1():
    print('s1')

def s2():
    print('s2')

this_module = sys.modules[__name__]

print(hasattr(this_module, 's1'))#存在返回True
getattr(this_module, 's2')()#执行方法名所对应的函数
程序调用此模块中的方法/属性

实现通过调用方法名实现对应方法

import time
#time.time()
while True:
    func_name=input('>>>')
    if func_name=='q' or func_name=='b':
        break
    else:
        print(getattr(time,func_name)())#注意括号中可传参
   
View Code

注意:一个模块中的类也可以被反射拿到。

setattr方法
class A:
    pass
a=A()
setattr(A,'name','eric')    #类属性
setattr(a,'name','ericcccc')#对象属性
print(A.name) 
print(a.name)
setattr:设置一个变量
delattr方法
class A:
    pass
a=A()
a.name='eriiccccc'
A.name='eric'
delattr(a,'name')
print(a.name)#由于对象中name被删除,所以找到类中的name
delattr:删除一个变量

注意:如果在删去A里面的name,再调用a.name时就会报错。

类的内置方法(双下方法)                                                         

__str__和__repr__                                                                                                                                

改变对象的字符串显示__str__,__repr__。
自定制格式化字符串__format__。

 eg:object  里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址。

l = [1,2,3,4,5]   # 实例化 实例化了一个列表类的对象
print(l)

注意:

  1. %s str() 直接打印 实际上都是走的__str__。
  2. %r repr() 实际上都是走的__repr__。
  3. print(obj) : '%s'%obj或者str(obj)的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么他返回的必定是一个字符串,如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__。
  4. repr(),只会找__repr__,如果没有找父类的。

class B:

     def __str__(self):
         return 'str : class B'

     def __repr__(self):
         return 'repr : class B'

b=B()
print('%s'%b)#执行了类内部的__str__方法,如果没有__str__方法则会调用__repr__方法。
print('%r'%b)#执行了类内部的__repr__方法,如果类中没有__repr__方法,则会返回一个对象。
%s和%r
format_dict={
    'nat':'{obj.name}-{obj.addr}-{obj.type}',#学校名-学校地址-学校类型
    'tna':'{obj.type}:{obj.name}:{obj.addr}',#学校类型:学校名:学校地址
    'tan':'{obj.type}/{obj.addr}/{obj.name}',#学校类型/学校地址/学校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'School(%s,%s)' %(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec='nat'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School('oldboy1','北京','私立')#实例化
print('from repr: ',repr(s1))#调用__repr__方法
print('from str: ',str(s1))#调用__str__方法
print(s1)#默认调用__str__方法

'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))#按指定格式输出
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))#若没有此格式,则默认按第一种
自定制格式化字符串

item系列                                                   

__getitem__\__setitem__\__delitem__                                                               

class Foo:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def __getitem__(self,item):
        if hasattr(self,item):
            return self.__dict__[item]
    def __setitem__(self,key,value):
        self.__dict__[key]=value
    def __delitem__(self,key):
        del self.__dict__[key]
    def __delattr__(self,item):
        self.__dict__.pop(item)
f=Foo('eric',20,'')
print(f.name)
print(f['name'])#通过字典形式获取属性值,必须在类中包含__getitem__(self,item)
f['age']=20#通过字典形式更改属性值,必须使用__setitem__(self,key,value)
print(f.age)
f['hobby']=''
print(f.hobby)
print(f.__dict__)
del f['hobby']#通过这种方式删除属性,必须使用__delitem__(self,key)
print(f.__dict__)
__getitem__\__setitem__\__delitem__

__del__                                   

析构方法,当对象在内存中被释放时,自动触发执行。
注:此方法一般无需定义,因为python是一门高级语言,程序员在使用时无需关心内存的分配和释放,因为此工作都是交给python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
del 对象.属性,这个方法其实是调用类内部的__delattr__(self,item)方法。

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

#输出结果
执行我啦
------->
__del__

 __new__                                   

构造方法:创建一个对象。

class A:
    def __init__(self):
        self.x=1
        print('in init function')
    def __new__(cls,*args,**kwargs):
        print('in new function')
        return object.__new__(A,*args,**kwargs)#此处返回值,返回至初始化中的self。
a=A()
print(a.x)
#new是创建self的机制
__new__

设计模式:23种

单例模式:限制一个类始终只能有一个实例

当你第一次实例化这个类的时候,就创建一个实例化的对象。当你之后再来实例化的时候,就用之前创建的那个对象。 

class A:
    __instance=False
    def __init__(self,name,age):
        self.name=name
        self.age=age
        
    def __new__(cls,*args,**kwargs):
        if cls.__instance:
            return cls.__instance
        cls.__instance = object.__new__(A)#此处返回值,返回至初始化中的self。
        return cls.__instance
a=A('egg',30)
a.cloth='小花袄'
b=A('gge',25)
print(a.__dict__)#通过打印,可以看到,b的实例化将A覆盖,有此属性的覆盖,没有此属性的继承
单例模式
class Singleton:
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls)
        return cls._instance

one = Singleton()
two = Singleton()

two.a = 3
print(one.a)
# 3
# one和two完全相同,可以用id(), ==, is检测
print(id(one))
print(id(two))
print(one == two)
print(one is two)
单例模式

__call__                                 

 当将类实例化后直接执行它则默认执行内部的__call__方法。

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

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

class A:
    def __init__(self,name):
        self.name = name
    def __call__(self):
        '''
        打印这个对象中的所有属性
        :return:
        '''
        for k in self.__dict__:
            print(k,self.__dict__[k])
a = A('alex')()
__call__

__len__                                  

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __len__(self):
        return len(self.__dict__)
a = A()
print(len(a))
__len__

__hash__                                

如果可hash(),其内部都实现了一个__hash__()方法,可以在自定义时,自己定义一个__hash__()方法,那么其值就可以由我们来控制。

通过加入__hash__方法,可以对具体的hash值进行条件设置。

class A:
    def __init__(self,name,sex):
        self.name=name
        self.sex=sex
    def __hash__(self):
        return hash(self.name+self.sex)
    
a=A('egg','')
b=A('egg','')
print(hash(a),hash(b))#这里不会根据属性hash,而是根据内存地址。
__hash__

__eq__                                 

class A:
    def __init__(self,name):
        self.name=name
    def __eq__(self,other):
        if self.name==other.name:#此处也可以写成__dict__
            return True
        return False
obj1=A('egg')
obj2=A('gge')
print(obj1==obj2)#==会触发__eq__方法
__hash__

上面直接比较(对象)时,比较内存地址(没有__eq__方法时)。当加入__eq__方法时,就可以进行重新方法定制。

内置的方法有很多,不一定全都在object中

class Classes:
    def __init__(self,name):
        self.name = name
        self.student = []
    def __len__(self):
        return len(self.student)
    def __str__(self):
        return 'classes'
py_s9= Classes('python全栈9期')
py_s9.student.append('二哥')
py_s9.student.append('泰哥')
print(len(py_s9))
print(py_s9)
View Code
class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    suits = ['红心','方板','梅花','黑桃']

    def __init__(self):
        self._cards = [Card(rank,suit) for rank in FranchDeck.ranks
                                        for suit in FranchDeck.suits]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]

deck = FranchDeck()
print(deck[0])
from random import choice
print(choice(deck))
print(choice(deck))
纸牌游戏
import json
from collections import namedtuple
Card=namedtuple('Card',['rank','suit'])
class FranchDeck:
    ranks=[str(n) for n in range(2,11)]+list('JQKA')
    suits=['红心','方板','梅花','黑桃']
    def __init__(self):
        self._cards=[Card(rank,suit) for rank in FranchDeck.ranks
                                        for suit in FranchDeck.suits]
    def __len__(self):
        return len(self._cards)
    def __getitem__(self,item):
        return self._cards[item]
    def __setitem__(self,key,value):
        self._cards[key]=value
    def __str__(self):
        return json.dumps(self._cards,ensure_ascii=False)
deck=FranchDeck()
print(deck[0])
from random import choice
print(choice(deck))#choice依赖内置的__len__方法
print(choice(deck))#抽牌
from random import shuffle
shuffle(deck)#洗牌,依赖于__setitem__方法
print(deck[:5])
纸牌游戏2
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):#比较hash相等
        return hash(self.name+self.sex)

    def __eq__(self, other):#通过eq进行条件判断
        if self.name == other.name and self.sex == other.sex:return True

p_lst = []
for i in range(84):
    p_lst.append(Person('egon',i,'male'))

print(p_lst)
print(set(p_lst))
面试题--去重

注意:set依赖hash和__eq__方法。

 

posted on 2019-12-08 14:51  眨眼星舟_Eric  阅读(113)  评论(0)    收藏  举报

导航