python之路27 -- 之面向对象进阶(反射)与双下方法

isinstance和issubclass

class A:
    pass
class B(A):
    pass
a = A()
print(isinstance(a,A))       #判断对象a是不是A类的对象,是则返回True,否则返回False
print(issubclass(B,A))       #判断B类是不是A类的子类,是则返回True,否则返回False

反射

1、什么是反射

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

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

  • 下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
  • python中的反射:是用字符串类型的名字,去操作变量
  • 反射的应用场景:知道有这个变量,但是我只能拿到和变量名一样的字符串,这种情况下就可以用反射的方法,反射对象中的属性及方法
2.1、反射对象的方法和属性
class A:
    def func(self):
        print('in func')
a = A()      #实例化a对象
a.name = 'ouyang'     #给a对象添加name属性
if hasattr(a,'func'):        #判断a对象下是否存在func这个变量,存在则返回True,不存在则返回False
    pub_func = getattr(a,'func')      #需要传对象,想要获取的变量
    pub_func()          #反射对象的方法,结果为:in func
    print(getattr(a,'name'))     #反射对象的属性,结果为:ouyang
2.2、反射类的属性
class A:
    price = 20      #类的静态属性
    def func(self):
        print('in func')
print(A.price)         #正常获取A类的price属性对应的值
if hasattr(A,'price'):     #判断A类是否存在price的属性
    print(getattr(A,'price'))      #用反射获取A类的price属性对应的值
2.3、反射类的方法
class A:
    price = 20     #静态属性
    @classmethod       #需要加类方法装饰器
    def func(self):    #类方法
        print('in func')
if hasattr(A,'func'):        #判断,如果存在A类及A类下func方法,如果存在则返回True,不存在则返回False
    getattr(A,'func')()      #反射A类下的func方法
2.4、反射模块下的方法

2.4.1、自定义一个模块my

day = 'monday'    #周一
def wahaha():
    print('my , wahaha')

2.4.2、反射模块的属性

import my
print(my.day)          #正常获取my模块的day属性
if hasattr(my,'day'):    #判断my模块是否有day属性
    print(getattr(my,'day'))      #反射获取获取my模块下的day属性

2.4.3、反射模块的方法

import my
my.wahaha()          #正常获取my模块下的wahaha方法
if hasattr(my,'wahaha'):    #判断my模块下是否存在wahaha方法
    getattr(my,'wahaha')()      #反射获取my模块下的wahaha方法
2.5、反射内置模块下的方法
import time      #导入time模块
if hasattr(time,'time'):         #判断
    print(getattr(time,'time')())       #反射
2.6、反射自己函数的变量
def uxuan():
    print('优选书城')
yisou = "易搜"
import sys
if hasattr(sys.modules[__name__],'yisou'):      #判断
    print(getattr(sys.modules[__name__],'yisou'))     #反射当前模块下的变量名
    getattr(sys.modules[__name__],'uxuan')()    #反射当前脚本下的函数名

2.7、设置一个变量(用的不多)

class A:
    pass
a = A()    #实例化一个对象a
setattr(a,'name','vip')      #给a对象添加一个name属性
setattr(A,'name','net')     #给A类添加了一个静态属性name
print(A.name)   #获取A类的name静态属性,结果为:net
print(a.name)    #获取a对象的name属性,结果为:vip

2.8、删除一个变量(用的不多)

class A:
    pass
a = A()
setattr(a,'name','vip')      #给a对象添加一个name属性
setattr(A,'name','net')     #给A类添加了一个静态属性name
print(A.name)
print(a.name)

#删除一个变量
delattr(a,'name')       #删除对象的name属性
print(a.name)         #对象里没有了name属性,对象去类里获取了name属性
delattr(A.name)          #删除类的name属性
print(a.name)           #没有了,在获取就报错了,TypeError: delattr expected 2 arguments, got 1

四个自省函数说明

hasattr:判断这个名字是否可以反射,可以反射则返回True,不可以反射则返回False

getattr:使用字符串类型的名字可以获取到变量的值

setattr:设置一个变量

delattr:删除一个变量

双下方法

1、__str__和__repr__方法

说明:使用str(a)的时候实际是调用的a对象里的.__str__方法,如果a对象里没有.__str__方法,就会到object里去找__str__方法,因为在python中所有类都是继承object类的

1.1、__str__方法

#1、了解__str__方法
class A:
    def __str__(self):
        return "A's object"
a=A()     #实例化一个对象a
print(a)    #打印一个对象的时候,就是调用:对象.__str__方法,如果对象里没有.__str__方法就默认继承父类,然后到object下的__str__方法,结果为:A's object
#a.__str__  --> object ,object里有一个__str__,一旦被调用,就会返回调用这个方法的对象的内存地址
print('%s:%s'%('A',a))     #%s 、str() 、直接打印实际上都是调用的.__str__方法,结果为:A:A's object

#2、__str__的正确打开方式
class Teacher:
    def __init__(self,name,salary):    #初始化方法
        self.name = name
        self.salary = salary
    def __str__(self):
        return "Teacher's object :%s"%self.name
ouyang = Teacher('欧阳',25000)
print(ouyang)   #打印一个对象的时候,就是调用:对象.__str__方法,结果为:Teacher's object :欧阳

1.2、__repr__方法

class Teacher:       #定义类
    def __init__(self,name,salary):      #初始化方法
        self.name = name
        self.salary = salary
    def __str__(self):       #__str__方法
        return "Teacher's object :%s"%self.name
    def __repr__(self):     #__repr__方法
        return str(self.__dict__)
ouyang = Teacher('欧阳',25000)      #实例化ouyang这个对象
print(ouyang)   #打印一个对象的时候,就是调用:对象.__str__方法,如果对象里没有.__str__方法,就默认去找父类的str方法,结果为:Teacher's object :欧阳
print(repr(ouyang))     #%r 、repr()实际上都是走的:对象.__repr__方法,结果为:{'name': '欧阳', 'salary': 25000}
print('...%r'%ouyang)     #结果为:...{'name': '欧阳', 'salary': 25000}

说明:

  • repr是str的备胎,即:有__str__则调用__str__的方法,没有__str__则调用__repr__的方法,__str__和__repr__方法都没有才会找父类的的str方法和repr方法
  • 反之:调用__repr__方法的时候,没有__repe__时不会调用__str__的方法
  • print(obj),'%s',str(obj)的时候:实际上是调用了obj.__str__方法,如果__str__方法有,那么他返回的必须是一个字符串(如果返回其他类型的数据就会报错)
  • 如果没有__str__方法,会先找本类中的__repr__方法,在没有再到父类中找__str__方法、然后到父类中找__repr__,直到object类里也没有就会报错了(object:内置__str__和__repr__)!!
  • repr():只会找对象.__repr__,如果没有就找父类,父类没有就找object类,object类里也没有就会报错
  • 如果再一个类里面只能实现__repr__或__str__一个方法,怎么实现:一定要实现__repr__,因为__repr__方法实现了之后可以给__str__用

2、__len__方法

前面说到__str__和__repr__方法在object类中存在,如果当前调用对象里没有就会去object类中去继承,这里的__len__方法默认object中没有!!

2.1、了解__len__方法

class A:
    def __len__(self):
        return 10
a = A()
print(len(a))     #结果为:10

class A:
    pass
a = A()
print(len(a))    #这里会报错,TypeError: object of type 'A' has no len(),说没有len方法

2.2、__len__方法的正确打开方式

class Classes:        #定义classes类
    def __init__(self,name):        #类的初始化方法
        self.name = name
        self.student = []
    def __len__(self):     #定义__len__方法
        return len(self.student)
py = Classes('python')     #实例化一个对象
py.student.append('李四')    #往py对象的student属性里追加内容
py.student.append('张三')
py.student.append('王五')
print(len(py))   #获取py对象的长度,结果为:3

3、析构方法,__len__方法

析构方法:即 再删除一个变量之前在做一些收尾的工作

class A:       #定义类
    def __del__(self):        #__del__方法
        print("执行我啦")
a = A()       #实例化a对象
del a      #del删除a对象,即执行了这个方法,又删除了变量(先执行方法,然后在进行删除操作(再删除一个变量之前在做一些收尾的工作))
print(a)    #删除变量后,再打印就报错了,NameError: name 'a' is not defined,说没有name a

说明:引用计数:即变量的引用次数为0时,这个变量会被python解释器删除。变量设置的次数超过python解释器的设置值时会被python解释器删除(了解即可),只要删除就一定会执行del函数

4、__call__方法

class A:      #定义A类
    def __init__(self,name):        #初始化方法
        self.name = name
    def __call__(self):       #__call__方法
        print("执行我啦")
a = A('ouyang')      #实例化一个a对象
a()       #对象():对象名+()相当执行call方法,结果为:执行我啦
A('ouyang')()     #相当于:对象(),结果为:执行我啦

5、item系列 

  • __getattr__
  • __setattr__
  • __delattr__
class Foo:
    def __init__(self,name,age,sex):       #初始化方法
        self.name = name
        self.age = age
        self.sex = sex
    def __getitem__(self, item):      #getattr方法
        if hasattr(self,item):
            return self.__dict__[item]
    def __setitem__(self, key, value):      #setattr方法
        # print(key,value)
        self.__dict__[key] = value
    def __delitem__(self, key):         #delattr方法
        del self.__dict__[key]
f = Foo('ouyang',22,'')      #实例化一个对象
print(f['age'])       #对象['']:方法会自动触发类里的__getitem__(self,item)方法,结果为:22
f['hobby'] = 'shou'   #对象['k'] = v:方法会自动触发类里的__setitem__(self, key, value)方法,添加属性
print(f.hobby,f['hobby'])    #对象的正常调用hobby属性和通过__getitem__(self,item)的方式调用属性,结果为:shou shou
# del f.hobby     #正常删除对象下属性的方法,del 对象.属性,会触发对象里的__delattr__方法,默认object类中有__delattr__方法
# print(f.__dict__)     #结果为:{'name': 'ouyang', 'age': 22, 'sex': '男'},发现hobby属性被删除了
del f['hobby']     #使用del 对象['属性']:方法时,会触发类里的__delitem__(self, key)方法
print(f.__dict__)    ##结果为:{'name': 'ouyang', 'age': 22, 'sex': '男'},发现hobby属性被删除了

6、__new__构造方法

class A:
    def __init__(self):     #3、然后执行__init__方法
        self.x = 1
        print('in init function')
    def __new__(cls, *args, **kwargs):       #2、实例化对象的时候首先执行__new__(对象,参数1,参数2)方法,
        print('in new function')
        return object.__new__(A,*args,**kwargs)       #2.1、借助object类的__new__方法生成A类并返回给对象;
a = A()        #1、实例化,结果为:in new function
                                in init function
print(a.x)    #结果为:1

6.1、画图说明

 

 7、设计模式与单例模式

设计模式:共 23种
单例模式:
一个类 始终 只有 一个 实例
当你第一次实例化这个类的时候 就创建一个实例化的对象
当你之后再来实例化的时候 就用之前创建的对象
单例模式实现
class A:
    _instance = False
    def __init__(self,name,age):     #3、然后执行__init__方法
        self.name = name
        self.age = age
    def __new__(cls, *args, **kwargs):       #1.1、实例化对象a1的时候首先执行__new__(对象,参数1,参数2)方法,2.1、实例化对象a时首先执行__new__方法
        if cls._instance:      #1.2、如果_instance为True,2.2、此时cls._instance == A对象,所以为True
            return cls._instance     #1.3、则返回:cls._instance,2.3、返回A对象
        cls._instance = object.__new__(A)       #1.4、没有匹配到上面的if则执行这里,借助object类的__new__方法创建了一个类,此时_instance == A对象
        return cls._instance     #1.5、返回cls._instance,此时_instance == A对象
a1 = A('net',23)      #1、实例化对象a1
a = A('ouyang',33)    #2、实例化对象a
a.cloth = '风衣'      #给对象a添加cloth属性
print(a1.name)      #结果为:ouyang
print(a.name)       #结果为:ouyang
print(a.cloth,a1.cloth)    #结果为:风衣 风衣

7.1、画图说明实现过程

8、eq方法

        触发条件:==

#eq方法:正常情况下判断两个对象是否相等是比较内存地址,可以通过eq方法进行定制,触发条件为:==(比较两个值)
class A:
    def __init__(self,name):
        self.name = name

    def __eq__(self, other):
        if self.name == other.name:      #或者:if self.__dict__ == other.__dict__:
            return True
        else:
            return False

ob1 = A('egon')
ob2 = A('egon')
print(ob1 == ob2)     #没有实现eq方法的时候默认就是比较两个对象的内存地址,使用eq方法可以定制比较对象的方法,结果为:True
obj1 = A('test')
obj2 = A('test1')
print(obj1 == obj2)     #使用==(比较两个值)时会触发__eq__方法,结果为:False

画图:

9、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('ouyang','')
b = A('ouyang','')
# print(hash(a),hash(b))     #默认情况下根据内存地址进行hash(hash值不一样),那么如何实现同一个类的对象的hash是一样的那?
print(hash(a),hash(b))     #使用hash()方法实际是调用的对象里的__hash__方法,如果对象里没有则继承父类里的hash方法,结果为:-5861893281255257818

 

posted @ 2018-09-30 09:27  欧-阳  阅读(254)  评论(0)    收藏  举报