面向对象进阶

__del__

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

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

class Foo:

    def __del__(self):
        print('执行我啦')

f1=Foo()
del f1
print('------->')

#输出结果
执行我啦
------->
# 构造方法 创建一个对象的
# 初始化方法 __init__ 给已经创建出来的对象添加属性
# 析构方法 删除一个对象的时候调用的方法
import time
class A:
    def __init__(self):
        self.f = open('userinfo','a')
    def consume(self):
        pass
    def __del__(self):
        '''在删除一个对象之前做一些收尾工作'''
        self.f.close()
        print('删除一个对象的时候调用我')

a = A()
time.sleep(1)
del a
# 删除一个对象的时候,如果内部存在__del__方法,
# 那么在删除一个对象之前先执行__del__方法中的代码
print(a)

__new__构造方法

new()是在新式类中新出现的方法,它作用在构造方法init()建造实例之前,可以这么理解,在Python 中存在于类里面的构造方法init()负责将类的实例化,而在init()调用之前,new()决定是否要使用该init()方法,因为new()可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。 
如果将类比喻为工厂,那么init()方法则是该工厂的生产工人,init()方法接受的初始化参 数则是生产所需原料,init()方法会按照方法中的语句负责将原料加工成实例以供工厂出货。而 new()则是生产部经理,new()方法可以决定是否将原料提供给该生产部工人,同时它还决定着出 货产品是否为该生产部的产品,因为这名经理可以借该工厂的名义向客户出售完全不是该工厂的产品。 
new()方法的特性: 
new()方法是在类准备将自身实例化时调用。 
new()方法始终都是类的静态方法,即使没有被加上静态方法装饰器。

类的实例化和它的构造方法通常都是这个样子:

1 class MyClass(object):
2    def __init__(self, *args, **kwargs):
3        ...
4 # 实例化
5 myclass = MyClass(*args, **kwargs)

正如以上所示,一个类可以有多个位置参数和多个命名参数,而在实例化开始之后,在调用 init()方法之前,Python首先调用new()方法:

def __new__(cls, *args, **kwargs):
   ...

第一个参数cls是当前正在实例化的类。 
如果要得到当前类的实例,应当在当前类中的new()方法语句中调用当前类的父类 的new()方法。 
例如,如果当前类是直接继承自object,那当前类的new()方法返回的对象应该为:

def __new__(cls, *args, **kwargs):
   ...
   return object.__new__(cls)

事实上如果(新式)类中没有重写new()方法,即在定义新式类时没有重新定义new()时 ,Python默认是调用该类的直接父类的new()方法来构造该类的实例,如果该类的父类也没有重写 new(),那么将一直按此规矩追溯至object的new()方法,因为object是所有新式类的基类。 


而如果新式类中重写了new()方法,那么你可以自由选择任意一个的其他的新式类(必定要是 新式类,只有新式类必定都有new(),因为所有新式类都是object的后代,而经典类则没有new() 方法)的new()方法来制造实例,包括这个新式类的所有前代类和后代类,只要它们不会造成递归死 循环。具体看以下代码解释:

 1 class Foo(object):
 2     def __init__(self, *args, **kwargs):
 3         ...
 4     def __new__(cls, *args, **kwargs):
 5         return object.__new__(cls, *args, **kwargs)    
 6 
 7 # 以上return等同于 
 8 # return object.__new__(Foo, *args, **kwargs)
 9 # return Stranger.__new__(cls, *args, **kwargs)
10 # return Child.__new__(cls, *args, **kwargs)
11 
12 class Child(Foo):
13     def __new__(cls, *args, **kwargs):
14         return object.__new__(cls, *args, **kwargs)

如果Child中没有定义new()方法,那么会自动调用其父类的new()方法来制造实例,即

Foo.__new__(cls, *args, **kwargs)

在任何新式类的new()方法,不能调用自身的new()来制造实例,因为这会造成死循环。因此必须避免类似以下的写法: 
在Foo中避免:return Foo.new(cls, *args, **kwargs)或return cls.new(cls, *args, **kwargs)。

使用object或者没有血缘关系的新式类的new()是安全的,但是如果是在有继承关系的两个类之间,应避免互调造成死循环,例如:(Foo)return Child.new(cls), (Child)return Foo.new(cls)。

class Stranger(object):
    ...

在制造Stranger实例时,会自动调用 object.new(cls) 
通常来说,新式类开始实例化时,new()方法会返回cls(cls指代当前类)的实例,然后该类的 init()方法作为构造方法会接收这个实例(即self)作为自己的第一个参数,然后依次传入new ()方法中接收的位置参数和命名参数。

注意:如果new()没有返回cls(即当前类)的实例,那么当前类的init()方法是不会被调用 的。如果new()返回其他类(新式类或经典类均可)的实例,那么只会调用被返回的那个类的构造方法。

class Foo(object):
    def __init__(self, *args, **kwargs):
        ...
    def __new__(cls, *args, **kwargs):
        return object.__new__(Stranger, *args, **kwargs)  

class Stranger(object):
    ...

foo = Foo()
print type(foo)  

打印的结果显示foo其实是Stranger类的实例。

因此可以这么描述new()和ini()的区别,在新式类中new()才是真正的实例化方法,为类提供外壳制造出实例框架,然后调用该框架内的构造方法init()使其丰满。 
如果以建房子做比喻,new()方法负责开发地皮,打下地基,并将原料存放在工地。而init()方法负责从工地取材料建造出地皮开发招标书中规定的大楼,init()负责大楼的细节设计,建造,装修使其可交付给客户。

单例模式

一个类 可以被多次实例化 但是同一时间在python的内存中,只能有一个实例

class A:
    _instance = None#定义一个静态变量
    def __init__(self,name):#初始化方法
        '''给娃穿衣服'''
        self.name = name#定义一个动态属性
    def __new__(cls, *args, **kwargs):#定义一个new方法
        '''生娃的过程'''
        if not A._instance:#如果没有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,a1 is a2)  # egon egon True
class Singleton:
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls)
        return cls._instance

one = Singleton()
two = Singleton()

two.a = 3
print(one.a)
# 3
# one和two完全相同,可以用id(), ==, is检测
print(id(one))
# 29097904
print(id(two))
# 29097904
print(one == two)
# True
print(one is two)

item系列

__getitem__\__setitem__\__delitem__

lass A:
    def __init__(self,name):#初始化方法
        self.name = name#定义一个静态属性
        self.age = 81
    def __getitem__(self, item):#查看一个对象的的方法
        return self.__dict__[item]
    def __setitem__(self, key, value):#增加和修改一个动态属性
        self.__dict__[key] = value
    def __delitem__(self, key):#删除一个动态属性
        del self.__dict__[key]
a = A('alex')#实例化
print(a['name'])  #alex 对应了类中一个方法的语法#a.name==a['name']
print(a['age'])  #81 对应了类中一个方法的语法#a.age==a['age']
# 增加 和 修改一个属性
a['sex'] = '不详'# a.sex = '不详'#增加一个a.sex='不祥'
print(a.__dict__)#{'name': 'alex', 'age': 81, 'sex': '不详'}
# print(a.sex)
# print(a['sex'])
a['sex'] = ''#修改一个对象的动态属性
print(a.__dict__)#{'name': 'alex', 'age': 81, 'sex': '女'}
del a['sex']#删除一个a['sex']的属性
print(a.__dict__)#{'name': 'alex', 'age': 81}

__call__

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

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

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


obj = Foo() # 执行 __init__
obj()       # 执行 __call__
class A:
    def __call__(self,a):
        print('执行我了',a)
    def call(self,b):
        print('执行我了',b)
a = A()
a('aaa')   # __call__
a.call('bbb')

hash

不可变的数据类型都可以被hash

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

    def __hash__(self):#定义一个魔术方法
        print(hash(str(12)))#同一时间字符串12的哈希值#7130726887503445041
        return hash(str(self.a)+str(self.b))#同一时间返回一个哈希值'12'#7130726887503445041
a = A()
print(hash(a))#返回对象的哈希值
class A:pass
    # def __hash__(self):
    #     return 1
a = A()
b = A()
print(hash(a))   # object.__hash__()#同一时间同一个对象的hash值不管调用几次都相同
print(hash(a))   # object.__hash__()
print(hash(a))   # object.__hash__()
print(hash(a))   # object.__hash__()
print(hash(a))   # object.__hash__()
print(hash(a))   # object.__hash__()
print(hash(a))   # object.__hash__()
print(hash(b))   # object.__hash__()

# dict的key   set的元素
# dic  key --> value
# dic[key] = value

# hash(obj)函数,obj对象对应的类必然内部实现了__hash__方法
# hash的结果就是__hash__方法的返回值
# 且在一次成的执行过程中是不会发生变化的
# 且要想作为字典的key或者作为集合的元素,这个对象对应的类必须实现__hash__方法

扑克牌例子

 1 from collections import namedtuple#引用一个collections模块namedtuple可命名元祖
 2 Card = namedtuple('Card',['rank','suit'])#创建一个可命名元祖
 3 # card1 = Card(1,'红桃')
 4 
 5 class FranchDeck:#创建一个类
 6     ranks = [str(n) for n in range(2,11)] + list('JQKA')#设定大小静态属性
 7     suits = ['红心','方板','梅花','黑桃']#设置花色静态属性
 8 
 9     def __init__(self):#初始化一个元祖属性
10         self._cards = [Card(rank,suit) for rank in FranchDeck.ranks
11                                         for suit in FranchDeck.suits]
12 
13     def __len__(self):#定义一个len方法
14         return len(self._cards)# 返回对象的_cards属性的个数
15 
16     def __getitem__(self, item):#查询方法
17         return self._cards[item]#返回一个对象属性的所有内容
18 
19     def __setitem__(self, key, value):
20         self._cards[key] = value
21 deck = FranchDeck()
22 print('**',deck[:])
23 from random import choice#引入选择模块
24 print(choice(deck))   # deck对象对应的类中的getitem方法和len方法
25 from random import shuffle#打乱顺序
26 shuffle(deck)#打印顺序
27 print('**',deck[:])

 

金融公司面试题

 1 # 有一个类,对应这个类产生了100个对象
 2 # 每个对象有三个属性 : 姓名 年龄 性别
 3 # 请对这一百个对象进行去重,如果姓名和性别相同,即便年龄不同也是相同的对象
 4 # 问最简便的方法?
 5 class Person:#定义一个类
 6     def __init__(self,name,age,sex):#初始化属性
 7         self.name = name#对象的name属性
 8         self.age = age#对象的age属性
 9         self.sex = sex#对象的sex属性
10     def __hash__(self):#定义哈希方法
11         return hash('%s%s'%(self.name,self.sex))#返回hash'self.name,self.sex'的值
12     def __eq__(self, other):#定义一个eq方法
13         if self.name == other.name and  
14             self.sex == other.sex:#如果对象属性等于
15             return True
16 p_lst = []#定义个空列表
17 for i in range(100):#打印0到99
18     p_lst.append(Person('egon',i,'male'))#p_lst列表添加Person('egon',i,'male')
19 p_lst.append(Person('alex',i,'male'))#p_lst列表添加Person('alex',i,'male')
20 p_lst.append(Person('yuan',i,'male'))#p_lst列表添加Person('yuan',i,'male')
21 print(p_lst)
22 print(set(p_lst)) # 报错不可hash 完成了__hash__
23 # hash是否相等   __hash__
24 # 值是否相等     __eq__
25 
26 # 收获1
27 # 对于一些python当中已经存在的内置函数 内置数据类型 内置模块中的方法
28 # 都有可能依赖于类中的内置方法
29 # 收获2
30 # set方法依赖集合中元素对象的__hash__ __eq__
原文地址:https://www.cnblogs.com/strive-man/p/8560327.html