铁乐学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://www.cnblogs.com/tielemao/p/8886258.html

时间: 2024-11-07 18:43:29

铁乐学python_day24_面向对象进阶1_内置方法的相关文章

Python之路(第二十六篇) 面向对象进阶:内置方法

一.__getattribute__ object.__getattribute__(self, name) 无条件被调用,通过实例访问属性.如果class中定义了__getattr__(),则__getattr__()不会被调用(除非显示调用或引发AttributeError异常) class Foo: ? def __init__(self,x): self.x = x ? def __getattr__(self, item): print("执行__getattr__") ?

Python之路(第二十七篇) 面向对象进阶:内置方法、描述符

一.__call__ 对象后面加括号,触发执行类下面的__call__方法. 创建对象时,对象 = 类名() :而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()() class Foo: ? def __call__(self, *args, **kwargs): print("我执行啦") ? f = Foo() f() #对象加括号调用执行类下的__call__方法 #输出结果 我执行啦 二.__next__和__iter__实现迭代器协议 迭

python全栈开发【第十七篇】面向对象反射和内置方法

一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被类调用,就像正常调用函数一样 类方法和静态方法的相同点:都可以直接被类调用,不需要实例化 类方法和静态方法的不同点: 类方法必须有一个cls参数表示这个类,可以使用类属性 静态方法不需要参数 绑定方法:分为普通方法和类方法 普通方法:默认有一个self对象传进来,并且只能被对象调用-------绑定

面向对象-反射-其他内置方法

1.反射 1.1反射含义 通过字符串的形式操作对象的相关属性.方法有hasattr,getattr,delattr #!/usr/bin/env python # -*- coding:utf-8 -*- # Author: vita class People: country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.n

面向对象的进阶---反射--一些内置方法

反射 反射和一些类的内置方法 1 isinstance ---- issubclass type()--判断 是不是 ininstance(object,cls) 判断 是不是类的对象 如果这个类有父类 这个对象也是这个父类的对象 issubclaaa(cls,cls) 判断一个类是不是另一个类的子类 ============================= 反射 ================================= 把一个字符串 类型的变量 变成一个 真实存在这个程序中的变量名

面向对象进阶:反射以及内置方法

一.反射 反射:使用字符串数据类型的变量名来获取这个变量的值 input:用户输入的如果是a,那么打印1.如果输入的是b那么就打印2.如果输入name,那么打印alex 文件:从文件中读出的字符串,想转换成变量的名字 网络:将网络传输的字符串转换成变量的名字 1.反射类中的变量:静态属性,类方法 # class Foo: # School = 'oldboy' # Country = 'China' # language = 'Chinese' # @classmethod # def clas

Python学习【第14篇】:面向对象之反射以及内置方法

面向对象之反射及内置方法 一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被类调用,就像正常调用函数一样 类方法和静态方法的相同点:都可以直接被类调用,不需要实例化 类方法和静态方法的不同点: 类方法必须有一个cls参数表示这个类,可以使用类属性 静态方法不需要参数 绑定方法:分为普通方法和类方法 普通方法:默认有一个self对象传进来,并且只能被

python开发函数进阶:命名空间,作用域,函数的本质,闭包,内置方法(globales)

一,命名空间 #局部命名空间#全局命名空间#内置命名空间 #三者的顺序#加载顺序 硬盘上--内存里#内置-->全局(从上到下顺序加载进来的)-->局部(调用的时候加载) 1 #!/usr/bin/env python 2 #_*_coding:utf-8_*_ 3 4 #全局命名空间 5 a = 5 6 b = 8 7 #局部命名空间(函数) 8 def my_max(): 9 c = a if a > b else b 10 return c 11 m = my_max() 12 pr

面向对象之反射及内置方法

一.静态方法(staticmethod)和类方法(classmethod) 类方法:有个默认参数cls,并且可以直接用类名去调用,可以与类属性交互(也就是可以使用类属性) 静态方法:让类里的方法直接被类调用,就像正常调用函数一样 类方法和静态方法的相同点:都可以直接被类调用,不需要实例化 类方法和静态方法的不同点: 类方法必须有一个cls参数表示这个类,可以使用类属性 静态方法不需要参数 绑定方法:分为普通方法和类方法 普通方法:默认有一个self对象传进来,并且只能被对象调用-------绑定