面向对象进阶(反射)

1、isinstance和issubclass
1)、isinstance(obj,cls)检查是否obj是否是类 cls 的对象
print(obj.__dict__) #由于报错无法执行

class Foo(object):
     pass
  
obj = Foo()
  
print(isinstance(obj, Foo)) #True

2)、issubclass(sub, super)检查sub类是否是 super 类的派生类

class Foo(object):
    pass
 
class Bar(Foo):
    pass
 
print(issubclass(Bar, Foo)) #True

 一、反射

1、什么是反射

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

2、面向对象中的反射的定义

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

 3、实现反射的4个内置函数

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

#内置函数:hasattr/getattr/setattr/delattr

class Foo:
    f = '类的静态变量'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say_hi(self):
        print('hi,%s'%self.name)

obj=Foo('egon',73)

#1、hasattr 检测是否含有某属性
print(hasattr(obj,'name'))      #True
print(hasattr(obj,'say_hi'))    #True

#2、getattr 获取属性
n=getattr(obj,'name')
print(n)                        #egon
func=getattr(obj,'say_hi')
func()                          #hi,egon

print(getattr(obj,'aaaaaaaa','不存在啊')) #不存在啊

#3、setattr 设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)             
#{'name': 'egon', 'age': 73, 'sb': True, 'show_name': <function <lambda> at 0x0000026061C8AAE8>}
print(obj.show_name(obj))
#egonsb

#4、delattr 删除属性
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show_name111')#不存在,则报错
#报错信息:delattr(obj,'show_name111')#不存在,则报错
4个方法的使用示例
class Foo(object):
     
    staticField = "old boy"
 
    def __init__(self):
        self.name = 'wupeiqi'
 
    def func(self):
        return 'func'
    
    @staticmethod
    def bar():
        return 'bar'
 
print(getattr(Foo, 'staticField'))  #old boy
print(getattr(Foo, 'func'))     #<function Foo.func at 0x00000250E23EAC80>
print(getattr(Foo, 'bar'))      #<function Foo.bar at 0x00000250E23EAD08>
类也是对象
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys

def s1():
    print('s1')

def s2():
    print('s2')

#获取当前模块的名字
this_module = sys.modules[__name__]

print(hasattr(this_module, 's1'))   #True
print(getattr(this_module, 's2'))   #<function s2 at 0x0000023BABB4AB70>
反射当前模块成员

 导入其他模块,利用反射查找该模块是否存在某个方法

#!/usr/bin/env python
# -*- coding:utf-8 -*-

def test():
    print('from the test')
module_test.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
"""
程序目录:
    module_test.py
    index.py
 
当前文件:
    index.py
"""

import module_test as obj

#obj.test()

# 判断test是否属于obj的属性或方法
print(hasattr(obj,'test'))  #True
getattr(obj,'test')()       #直接执行test()方法
#结果:from the test
index.py

 反射总结

# hasattr  判断某一个 变量 是否能够.调用一个名字,返回True或者False
# getattr  直接获取一个变量中的名字的值
# setattr  为一个变量增加或者修改一个属性
# delattr  删除一个变量中的属性或者方法

# 反射类中的名字
# getattr(类名,'静态属性')
# getattr(类名,'类方法')()
# getattr(类名,'静态方法')()

# 反射对象中的名字
# getattr(对象名,'对象属性')
# getattr(对象名,'方法名')()

# 反射模块中的名字
# import 模块名
# getattr(模块名,'模块中的变量')
# getattr(模块名,'模块中的函数')()
# getattr(模块名,'模块中的类名')

# 反射当前模块中的名字
# import sys
# getattr(sys.modules[__name__],'变量')
# getattr(sys.modules[__name__],'函数')()
# getattr(sys.modules[__name__],'类名')
反射总结公式

 二、特殊的内置方法函数

1、__str__和__repr__

改变对象的字符串显示__str__,__repr__

自定制格式化字符串__format__

#_*_coding:utf-8_*_

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))       #from repr:  School(oldboy1,北京)
print('from str: ',str(s1))         #from str:  (oldboy1,北京)
print(s1)                           #(oldboy1,北京)

'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__没有被定义,那么就会使用__repr__来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
print(format(s1,'nat'))         #oldboy1-北京-私立
print(format(s1,'tna'))         #私立:oldboy1:北京
print(format(s1,'tan'))         #私立/北京/oldboy1
print(format(s1,'asfdasdffd'))  #oldboy1-北京-私立
View Code
class B:
    def __str__(self):
        return 'str : class B'

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

b=B()
print('%s'%b)   #str : class B
print('%r'%b)   #repr : class B
%s与%r

 2、__del__

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

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

# 构造方法 创建一个对象的初始化方法 __init__ 给已经创建出来的对象添加属性
# 析构方法 删除一个对象的时候调用的方法
class A:
    #初始化方法
    def __init__(self):
        pass
    #析构方法
    def __del__(self):
        print('删除一个对象是调用我')

a = A()
del a
#输出结果:
删除一个对象是调用我
View Code
import time
class A:
    #初始化方法
    def __init__(self):
        pass
    #析构方法
    def __del__(self):
        '''在删除一个对象之前做一些收尾工作'''
        print('删除一个对象的时候调用我')

a = A()
time.sleep(1)
#输出结果:
删除一个对象是调用我

# 删除一个对象的时候,如果内部存在__del__方法,
# 那么在删除一个对象之前先执行__del__方法中的代码
View Code

3、item系列

__getitem__/__setitem__/__delitem__
#__xxxitem__:使用 [''] 的方式操作属性时被调用

class Foo:
    #初始化方法
    def __init__(self,name):
        self.name=name

    #当访问不存在的属性时会调用该方法
    def __getitem__(self, item):
        print(self.__dict__[item])

    #:每当属性被赋值的时候都会调用该方法,因此不能再该方法内赋值 self.name = value 会死循环
    def __setitem__(self, key, value):
        self.__dict__[key]=value

    #当删除属性时调用该方法    
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)

    #删除指定对象的指定名称的属性
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

#实例化对象
f1=Foo('sb')
f1['age']=18
f1['age1']=19
print(f1.__dict__)  #{'name': 'sb', 'age': 18, 'age1': 19}
del f1.age1         #del obj.key时,我执行
del f1['age']       #del obj[key]时,我执行
f1['name']='alex'
print(f1.__dict__)  #{'name': 'alex'}
View Code

4、__new__

构造方法,当实例化对象时,会优先自动执行该方法

# new一个对象
# object.__new__()
class A:
    def __init__(self):
        print('执行init方法')
    def __new__(cls):
        print('执行new方法')
        #创造对象 将对象返回
        return object.__new__(cls)

a = A()
#输出结果:
# 执行new方法
# 执行init方法
print(type(a))  #<class '__main__.A'>
print(type(A))  #<class 'type'>

# 先执行__new__方法 创造出一个对象
# 然后把创造出来的对象传递给__init__方法
# 会把self自动的返回,被a接收


# 元类
# 有一个元类 在创建类
# type()  所有直接用class创建出来的类的元类都是type

# class 类名(B,classMeta = 元类名)
# class 类名(B,classMeta = type)  # 默认

# 元类 创造 类     所以所有的类的type都是它的元类,默认是type
# 类   创造 对象   具体创造对象的方法 __new__方法,所有的对象的type都是它对应的类

# python中 一切皆对象
# 变量 都有它属于的数据类型
View Code
#单例模式
class Singleton:
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls, *args, **kw)
        return cls._instance

#实例化对象
one = Singleton()
two = Singleton()

two.a = 3
print(one.a)    # 3
# one和two完全相同,可以用id(), ==, is检测
print(id(one))  # 2363725443368
print(id(two))  # 2363725443368
print(one == two)   # True
print(one is two)   # True
单例模式一
# 设计模式
# 单例模式
# 一个类 可以被多次实例化 但是同一时间在python的内存中,只能有一个实例
class A:
    _instance = None    #静态变量
    #初始化方法
    def __init__(self, name):
        '''给娃娃穿衣服'''
        self.name = name
    #构造方法    
    def __new__(cls, *args, **kwargs):
        '''生娃的过程'''
        if not A._instance:
            A._instance = object.__new__(cls)
        return A._instance

a1 = A('alex')  #第一次实例化的时候创造一个实例
print(a1.name)  #alex
a2 = A('egon')
print(a1.name, a2.name) #egon egon


#第二种方法
class B:
    #初始化方法
    def __init__(self,name):
        '''给娃穿衣服'''
        self.name = name
    #构造方法
    def __new__(cls, *args, **kwargs):
        '''生娃的过程'''
        if not hasattr(B, '_instance'):
            B._instance = object.__new__(cls)
        return B._instance

a1 = B('alex')  # 第一次实例化的时候创造一个实例
print(a1.name)      #alex
a2 = B('egon')
print(a1.name,a2.name)  #egon egon
单例模式二

 5、__call__

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

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

class Foo:
    #初始化方法
    def __init__(self):
        print('执行__init__')
    #构造方法
    def __call__(self, *args, **kwargs):
        print('执行__call__')
    #常规方法
    def call(self):
        print('执行call')

obj = Foo() # 执行__init__
obj()       # 执行__call__
obj.call()  # 执行call
View Code

6、__len__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
    
    def __len__(self):
        return len(self.__dict__)

a = A()
print(len(a))   #2
View Code

7、__hash__

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

    def __hash__(self):
        return hash(str(self.a)+str(self.b))

a = A()
print(hash(a))  #-8485950673113911502
View Code

8、__eq__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2
    #是==的内置方法
    def __eq__(self,obj):
        if  self.a == obj.a and self.b == obj.b:
            return True

a = A()
b = A()
print(a == b)   #True
View Code
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]

#纸牌实例化
deck = FranchDeck()
print(deck[0])      #Card(rank='2', suit='红心')

#随机抽牌
from random import choice
print(choice(deck))     #Card(rank='A', suit='红心')
print(choice(deck))     #Card(rank='K', suit='方板')
纸牌游戏
#可命名元组
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

#实例化纸牌对象
deck = FranchDeck()
print(deck[0])      #Card(rank='2', suit='红心')

#随机抽牌
from random import choice
print(choice(deck))     #Card(rank='7', suit='梅花')
print(choice(deck))     #Card(rank='Q', suit='梅花')

#随机洗牌
from random import shuffle
shuffle(deck)           
print(deck[:5])         
#输出结果
#[Card(rank='2', suit='红心'), Card(rank='8', suit='梅花'), Card(rank='6', suit='黑桃'), 
# Card(rank='5', suit='方板'), Card(rank='5', suit='梅花')]
纸牌游戏2
原文地址:https://www.cnblogs.com/lioushell/p/8549702.html