python_day24_面向对象进阶1_内置方法详解编程语言

python_day24_面向对象进阶1_内置方法

题外话1: 学习方法【wwwh】

what where why how
是什么,用在哪里,为什么,怎么用

学习到一个新知识点的时候,多问问上面的四个问题,并了解和实践透彻。

什么是反射?

先不说枯燥的概念,你可以自己总结出来,对于python中的反射,什么时候会用到?
需要使用字符串数据类型的变量名来使用变量的时候用到反射。
(通过字符串的形式操作对象相关的属性和方法。)
关键词:字符串
使用到字符串的场景多在:
1)文件中存储的是字符串,
2)网络上能传递的也最接近字符串,
3)用户输入的(用的不多,因为毕竟有安全隐患)

今天来学习内置方法的用处

(重写内置方法应用于个别需求)

__len__  len(obj) 
obj对应的类中含有__len__方法,len(obj)才能正常执行。 
__hash__ hash(obj)  是object类自带的 
只有实现了__hash__方法,has(obj)才能正常执行。 
题外话2: 列表和字典 
需要循环遍历去寻找的用列表比字典好一点,例如遍历文件中保存的用户名和密码。 
而需要快速由一个key找到一个值的,用字典好,比如用户登录状态。 
__str__内置方法 和__repr__内置方法 
print() # 向文件中写 print替你将数据类型转换成字符串打印出来。 
print(obj) 的结果是obj.__str__()的结果。 
str(obj) 的结果也是obj.__str__()的结果  (str是一个类名)。 
'%s' % obj 的结果是也是obj.__str__()的结果。 
repr(obj) 的结果和obj.__repr__()是一样的。 
'%r' % obj 的结果和obj.__repr__()是一样的。 
所有的输出本质上都是往文本上写。 
print(lis)  ---> lis.__str__() 
object类中的__str__就是默认返回一个数据的内存地址。 
自己新建的类重写一下自己类中的__str__内置方法就可以人性化的输出。 
重写内置方法为的是调用内置函数时输出的是自己想要的结果。 
reps(1) 和 reps('1') 打印的结果不一样, 
是因为class int 和class str 中的 __repr__方法不一样。 
默认设计是让你区分不同。 
当需要使用__str__的场景时,找不到__str__就找__repr__, 
当需要使用__repr__的场景时,找不到__repr__的时候就找父类的repr。 
双下repr是双下str的备胎。 
len() obj.__len__()  返回值是一致的 
不如这样说:len() 的结果是依赖 obj.__len__() 
同理 hash()的结果是依赖 obj.__has__() 
str() 的结果是依赖 obj.__str__() 
print(obj) 的结果是依赖 obj.__str__() 
%s 的结果是依赖 obj.__str__() 
repr 的结果是依赖 obj.__repr__() 
%r 的结果是依赖 obj.__repr__() 
repr是str的备胎。 
题外话3: 语法糖很甜 
凡是不以函数调用方式出现的给予你便利的语法方式,都可以称为语法糖。 
表面上语法只是很简单的方式,实际上后台都会落实到具体的方法上。 
__format__方法 
当你自己去实现双下format的时候,需要带有format_spec参数。 
format_spec参数 :格式化的标准(规则),format内置函数规定的。 
format_spec 在外部定义一个标准,为了让用户更灵活定制。 
def format(*args, **kwargs): # real signature unknown 
""" 
    Return value.__format__(format_spec) 
     
    format_spec defaults to the empty string 
    """ 
pass 
例: 
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)) 
print('from str: ',str(s1)) 
print(s1) 
''' 
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')) 
题外话4:为什么要归一化设计呢? 
1)更接近面向函数编程 
2)简单且节省代码 
__call__内置方法: 
class Teacher(): 
def __call__(self): 
print(123) 
t = Teacher() 
t() 
对像名加(),相当于调用类内置的__call__ 
callable 检测对象能否被调用,返回布尔值。 
一个对象是否可调用,取决于这个对象对应的类是否实现了__call__。 
如果对象拥有内置双下call方法,就可以被调用了。 
对象后面加括号,触发执行。 
注:构造方法的执行是由创建对象触发的,即:对象 = 类名() ; 
而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()() 
__eq__内置方法: 
判断 == 这件事是由__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) 
__del__ 析构方法 
所有面向对象语言都有的; 
在删除一个对象的时候做一些收尾工作,比如说关闭一些打开的文件,断开一些网络链接等。 
析构方法,当对象在内存中被释放时,自动触发执行。 
注:此方法一般无须定义,因为Python是一门高级语言,程序员在使用时无需关心内存的分配和释放, 
因为此工作都是交给Python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。 
class A: 
def __init__(self): 
self.f = open('File','w') 
del __del__(self): 
self.f.close 
print('执行我啦') 
a = A() 
del a 
print('666') 
运行完一个程序之后,python解释器会执行回收内存,自动运行双下del。 
__new__ 构造方法 
实例化的时候,init初始化之前,有一个创造对象的过程, 
创造对象过程就是用内置的双下new方法, 
先把小孩生下来才能给他穿衣服,先有对象再有初始化。 
设计模式---单例模式(考到了双下new方法) 
单例模式就是一个类只能有一个实例。 
可以被多次实例化,但最终只会有一个实例存在。 
# 使用双下new实现单例模式如下: 
class Teacher(): 
__instance = None 
def __init__(self, name, age): 
self.name = name 
self.age = age 
def __new__(cls, *args, **kwargs): 
if cls.__instance is None: 
obj = object.__new__(cls) 
cls.__instance = obj 
return cls.__instance 
a = B('alex', 80) 
b = B('egon', 20) 
print(a) 
print(b) 
print(a.name) 
print(b.name) 
item 系列 对象使用中括号的形式去操作(属性) 
__getitem__/__setitem__/__delitem__ 
[]中括号的形式,字典的通过key找值的方式,通过双下getitem实现 
__getitem__ 查询  对象名[属性名] 等同对象名.属性名 
__setitem__ 设置  对象名[属性名]= 值  赋值和修改,新增 
__delitem__ 删除  del 对象名[属性名] 等同del 对象名.属性 
也相当于执行了__delattr__ 
例:扑克牌游戏 
class FranchDeck: 
ranks = [str(n) for n in range(2, 11)] + list('JQKA') 
# 直接拼接两个列表生成13张扑克牌,并且里面的元素同时转为字符串 
suits = ['红心', '方块', '梅花', '黑桃'] 
def __init__(self): 
self.cards = [(rank, suit) for rank in FranchDeck.ranks 
for suit in FranchDeck.suits] 
# 元组中的点数和花色分别推导循环得出 
# [('2', '红心'), ('2', '方块'), ('2', '梅花'), ('2', '黑桃'), ('3', '红心')……] 
def __len__(self): 
return len(self.cards) 
# 统计扑克牌一共52张 
def __getitem__(self, item): 
return self.cards[item] 
# 等同于列表切片,取出当中的元素 
def __setitem__(self, key, value): 
self.cards[key] = value 
deck = FranchDeck() # 实例化 
# 对列表对象切片取值,下面两行是相同的 
print(deck[0]) 
print(deck.__getitem__(0)) 
print(deck.ranks) 
print(deck.cards) 
# 对列表统计元素,下面两行也是相同结果 
print(len(deck)) 
print(deck.__len__()) 
# 随机选择扑克牌 
from random import choice 
print(choice(deck)) 
print(choice(deck)) 
# 设置对应索引位置为什么值 
deck.__setitem__(0, ('3', '黑桃')) 
deck[1] = ('3', '梅花') 
print(deck[0]) # 取到的值不是红心2了,变成了黑桃3 
print(deck[1]) # 取到的值不是方块2了,变成了梅花3 
# shuffle = 洗牌,但每一次都要重新洗牌,不洗的话取出的是同一组 
from random import shuffle 
shuffle(deck) 
print(deck[:5]) 
shuffle(deck) 
print(deck[:13]) 
shuffle(deck) # 洗牌,然后切片发牌,将52张按序切完就可以玩扑克啦 
print(deck[:13]) 
print(deck[13:26]) 
print(deck[26:39]) 
print(deck[39:52]) 
# [('7', '黑桃'), ('A', '方块'), ('Q', '梅花'), ('3', '梅花'), ('10', '方块'), ('K', '红心'), ('8', '黑桃'), ('J', '方块'), ('2', '黑桃'), ('5', '方块'), ('Q', '方块'), ('5', '红心'), ('9', '红心')] 
# [('J', '梅花'), ('Q', '红心'), ('3', '红心'), ('4', '梅花'), ('10', '黑桃'), ('10', '梅花'), ('K', '黑桃'), ('7', '红心'), ('2', '梅花'), ('A', '梅花'), ('J', '黑桃'), ('2', '方块'), ('3', '黑桃')] 
# [('7', '梅花'), ('K', '方块'), ('6', '红心'), ('J', '红心'), ('A', '红心'), ('4', '方块'), ('8', '方块'), ('4', '黑桃'), ('5', '梅花'), ('9', '黑桃'), ('3', '方块'), ('Q', '黑桃'), ('8', '红心')] 
# [('4', '红心'), ('9', '梅花'), ('9', '方块'), ('6', '方块'), ('A', '黑桃'), ('6', '梅花'), ('10', '红心'), ('6', '黑桃'), ('K', '梅花'), ('5', '黑桃'), ('7', '方块'), ('2', '红心'), ('8', '梅花')] 
面试题: 
100个同一个类的对象, 
Person 对象有同名,同性别,但不同年龄,进行去重。 
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))

end
2018-4-19

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/12756.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论