python面向对象进阶

类相关的内置函数
isinstance
内置函数
isinstance(obj,cls) 检查obj是否是cls的对象
class C:pass
a = C()
print(isinstance(a,C)) 返回布尔类型

issubclass
issubclass(sub,super)检查sub是否是super的派生类
class B(C):
pass
print(issubclass(B,C)) 返回布尔类型

反射

1 什么是反射

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

 

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

四个可以实现自省的函数

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

四个方法:hasattr getattr setatte delattr

hasattr 和 getattr 组合使用

import mymodul  # 是自定义模块
if hasattr(mymodul,'Modul_cls'): #判断是否存在这个类名再执行后面的
print(getattr(mymodul,'Modul_cls')) # 获取到一个类的对象
#<class 'mymodul.Modul_cls'>

class A:
def func(self):
print('in func')
a = A()
a.name = 'alex'
a.age = 63
A.func(a)
# 反射对象的属性
ret = getattr(a,'age') # 通过变量名的字符串形式取到的值
print(ret)
# 反射对象的方法
print(getattr(a,'func'))
# 反射类的方法
print(getattr(A,'func'))
反射模块的属性

import mymodul
print(getattr(mymodul,'year'))

# 反射模块的方法
getattr(mymodul,'fangfa')() # 获取到的是一个函数名加()就是调用
# 内置模块也能用

# 反射自己模块中的变量和模块
def func():
print('自己模块名')

import sys
print(sys.modules['__main__']) # 这里要使用__name__如被其他脚本导入后使用用__name才能生效此方法
im = input('>>>')
getattr(sys.modules[__name__],im)()
要反射的函数有参数
import time
time.strftime('%Y-%m-%d %H:%M:%S')
getattr(time,'strftime')('%Y-%m-%d %H:%M:%S')
def qqx():pass

import sys
print(sys.modules['__main__']) #__name__

setattr # 设置修改变量
class D:pass
a = D()
setattr(a,'name','classtor') # 往对象a中添加一个静态属性
print(a.name)
setattr(D,'name',123)
print(D.name) # 往类D 中添加静态属性

delattr 删除属性
delattr(a,'name') # 删除了对象a属性中的name 打印类中的属性 123
print(a.name) # 123
delattr(D,'name')
print(a.name) # 删除了类的name属性再打印找不到会报错
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)

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

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

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

#设置属性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)
print(obj.show_name(obj))

#删除属性
delattr(obj,'age')
delattr(obj,'show_name')
delattr(obj,'show_name111')#不存在,则报错

print(obj.__dict__)
四个方法的其他演示
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')
print getattr(Foo, 'func')
print getattr(Foo, 'bar')
类也是对象
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import sys


def s1():
    print 's1'


def s2():
    print 's2'


this_module = sys.modules[__name__]

hasattr(this_module, 's1')
getattr(this_module, 's2')
反射当前模块成员
#!/usr/bin/env python
# -*- coding:utf-8 -*-

def test():
    print('from the test')

#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
"""
程序目录:
    module_test.py
    index.py
 
当前文件:
    index.py
"""

import module_test as obj

#obj.test()

print(hasattr(obj,'test'))

getattr(obj,'test')()
导入其他模块,利用反射查找该模块是否存在某个方法
 
__str__ 和 __reper__ 

改变对象的字符串显示__str__ 

__repr__ 原型毕露

自定制格式化字符串__format__

class A:
# def __str__(self):
# return '执行我啦'
def __init__(self,name,age):
self.name = name
self.age = age
def __repr__(self):
return '执行我啦reper'
def fund(self):
print('执行我啦')
a = A('alex',98)
print(a) # 打印一个对象时,实际是执行对象中__str__方法,没有返回内存地址
# <__main__.A object at 0x01E01DF0>
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(s1)
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)

lst = [1,2,3,4,5] # 实例化列表类方法
print(lst) # 实际调用lst下的__str__方法 即obj对象
%s 和 %r
class B:

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

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


b=B()
print('%s'%b)
print('%r'%b)

小结:
双下方法
内置的类方法和内置的函数之间有着千丝万缕的联系
__str__
__repr__ 原型毕露
object 里有一个__str__,一旦被调用,就返回调用这个方法的对象的内存地址
repr 是 str的备胎
str 不能做 repr 的备胎
print(obj)、%s 、str(obj) 的时候,实际上是内部调用了obj.__str__方法,如果str方法有,那么返回的必定是一个字符串,其他会报错
如果没有__str__方法,会先找本类中的__repr__方法,再没有再找父类中的__str__,如没有就会调用object中的__srt__方法会打印一个内存地址
repr() 只会找__repr__,如果没有找父类的

item系列
__getitem__ 和 __setitem__和__delitem__

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

def __getitem__(self, item):
print(self.__dict__[item])

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__)
del f1.age #执行对象中__delitem__方法
print(f1.__dict__)
del f1['age1'] #对象中有__delattr__方法,可以使用此方式进行删除
print(f1.__dict__)
f1['name']='alex'
print(f1.__dict__) #对象中有__getitem__方法,可以使用此方式进行删除

 __del__

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

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

class A:
def __del__(self): # 析构函数
print('执行我啦')

a = A()
del a # 执行了__del__此方法,有把a这个对象给删除了
print(a.__dict__) # 所以这里打印是会报错哦

__new__ 构造方法:创建一个对象
一个类始终只有一个实例
当你第一个次实例化这个类的时候,就创建一个实例化的对象
当你之后再来实例化的时候,就用之前创建的对象
单列模式
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) # cls 也可以写这个
return cls.__instance
egnon nezha 完全相同可以使用 id() == is 检测
egon = A('egon',38)
egon.cloth = '小花衣'
nezha = A('nezha',25)
print(nezha)
print(egon)
print(nezha.name)
print(egon.name)
print(egon == nezha) # True
print(egon is nezha) # True 对比内存地址
__call__

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

class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):

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

with和__enter__,__exit__
with 语句
class A:
def __enter__(self):
print('before')

def __exit__(self, exc_type, exc_val, exc_tb):
print('after')
with A() as a:
print('123')
# before
# 123
# after

with 和文件操作
# with 和文件操作
class Myfile:
def __init__(self,path,mode='r',encoding = 'utf-8'):
self.path = path
self.mode = mode
self.encoding = encoding

def __enter__(self):
self.f = open(self.path, mode=self.mode, encoding=self.encoding)
return self.f

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()


with Myfile('file',mode='w') as f:
f.write('wahaha') # 在本目录创建了file文件并写入了wahaha

with 和 pickle
import  pickle
class MyPickledump:
def __init__(self,path):
self.path = path

def __enter__(self):
self.f = open(self.path, mode='ab')
return self

def dump(self,content):
pickle.dump(content,self.f)

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()

class Mypickleload:
def __init__(self,path):
self.path = path

def __enter__(self):
self.f = open(self.path, mode='rb')
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self.f.close()

def load(self):
return pickle.load(self.f)

def loaditer(self):
while True:
try:
yield self.load()
except EOFError:
break

# with MyPickledump('file') as f:
# f.dump({1,2,3,4}) #以ab模式把1/2/3/4写入了file文件内

with Mypickleload('file') as f:
for item in f.loaditer():
print(item)

with 和 pickle 和iter
import  pickle
class MyPickledump:
    def __init__(self,path):
        self.path = path

    def __enter__(self):
        self.f = open(self.path, mode='ab')
        return self

    def dump(self,content):
        pickle.dump(content,self.f)

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()

class Mypickleload:
    def __init__(self,path):
        self.path = path

    def __enter__(self):
        self.f = open(self.path, mode='rb')
        return self


    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()

    def __iter__(self):
        while True:
            try:
                yield  pickle.load(self.f)
            except EOFError:
                break
# with MyPickledump('file') as f:
#      f.dump({1,2,3,4})
with Mypickleload('file') as f:
    for item in f:
        print(item)
__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__ 方法
class H:
def __init__(self,name,sex):
self.name = name
self.sex = sex
def __hash__(self):
return hash(self.name+self.sex)
a = H('egon','男')
b = H('egon','男')
print(hash(a)) # 608824368
print(hash(b)) # 608824368 两个对象的HASH一致
#
100名字 和 性别相同,就认为是一个对象去重 你年龄不同
set 去重对象
class A:
def __init__(self,name,sex,age):
self.name = name
self.sex = sex
self.age = age
def __hash__(self):
sec = hash(self.name+ self.sex)
return sec
def __eq__(self, other):
if self.name == other.name and self.sex == self.sex:
return True
return False

a = A('egg','男',38)
b = A('egg','男',37)
print(a,b)
print(set((a,b))) # set 方法去重对象时,依赖对象的__hash__ 和 __eq__ 方法

__eq__
class C:
def __init__(self,name):
self.name = name
def __eq__(self, other):
if self.name == other.name:
return True
else:
return False
# print(self,other)

ob1 = C('egg')
ob2 = C('egg')
print(ob1 == ob2) # False

# 纸牌游戏
import _json
from collections import namedtuple
Card = namedtuple('Card',['rank','suit']) # rank牌面的大小 suit 牌面的花色
ranks = [str(n) for n in range(2,11)] + list('JQKA')
suits = ['红心','方板','梅花','黑桃']
print(ranks)
lei = [Card(rank,suit) for rank in ranks for suit in suits]
print(lei)

class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA') # 生成2到10 和J到A 字符串类型的类表
    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])  # 对象中有__getitem__方法可以使用索引方式查看属性
# Card(rank='2', suit='红心')
from random import choice
print(choice(deck))  # choice取随机数牌、
# Card(rank='9', suit='方板')
print(choice(deck))
# Card(rank='2', suit='方板')
from random import shuffle
shuffle(deck)
shuffle(deck)
print(deck[:5]) # 洗牌用到的,依赖对象的__len__方法
纸牌游戏
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])
from random import choice
print(choice(deck))
print(choice(deck))

from random import shuffle
shuffle(deck)
print(deck[:5])
纸牌游戏2
class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):
        return hash(self.name+self.sex)

    def __eq__(self, other):
        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))
一道面试题

 








 

 

posted @ 2020-08-12 22:24  TinaLi  阅读(119)  评论(0)    收藏  举报