魔术方法(五)

问题一:__init__有什么作用?

在创建对象的时候,自动调用对创建的对象进行初始化设置的

问题二:什么是魔术方法?

在python中像__init__这类以双下滑线开头和结尾的方法,我们把它统称为魔术方法,也有人叫作魔法方法、特殊方法。

注意点:

魔术方法都是Python内部定义的(内置的),自己不要去定义__init__这类双下划线开头的方法。

问题三:创建对象,调用的第一个方法是什么?

__new__方法

定义的类默认继承object,在object父类里有个__new__()方法,这个new方法创建并返回一个新的对象,如下图:

当我们调用类的时候,会自动触发这个__new__()方法,当我们定义类的时候,没有__new__()方法,就会去object父类去查找,创建一个对象并返回。

ps: python内置函数的看不到源码,只能看到一些相关说明,内置函数全部c语言实现

class Hero(object):
  def __init__(self, name):
    self.name = name
    print('这是init方法')
  
  def __new__(cls, *args, **kwargs):
    print('这是new方法')
    
hero = Hero('sinder')
print(hero)

执行结果:

为什么创建的对象是None呢?

自己定义__new__()方法,创建对象时,自动触发自定义的__new__()方法去创建对象,但是我们自己定义的__new__()方法里没有实现如何创建对象的代码 ,所以对象是None。

所以自己重写__new__()方法时,需要返回一个对象。

class Hero(object):
  def __init__(self, name):
    self.name = name
    print('这是init方法')
  
  def __new__(cls, *args, **kwargs):
    print('这是new方法')
    return super().__new__(cls)
    
hero = Hero('sinder')
print(hero)

参数cls:代表对象自身

执行结果:

对象创建后才会执行__new__()

new方法的应用场景:单例模式

2、代码实现:

class MyClass(object):
  """单例模式类"""
  count = 0
  instance = None
  
  def __new__(cls, *args, **kwargs):
    if not cls.instance:
      #调用父类的new方法,创建一个对象
      cls.instance = object.__new__(cls)
      return cls.instance
    else:
      return cls.instance
    
h1 = MyClass()
h2 = MyClass()
print(id(h1), id(h2))

执行结果:

3、上下文管理器

上下文管理器是一个python对象,为操作提供了额外的上下文信息,这种额外的信息,在使用with语句初始化上下文,以及完成with块中的所有代码时,才用可调用的形式。

在一个类中,只要实现了__enter__和__exit__方法,那么该类就实现了上下文管理协议。

class MyOpen(object):
  """
  自定义的上下文管理器类
  """
  def __init__(self, file_name, method, encoding):
    self.file_name = file_name
    self.method = method
    self.encoding = encoding
    self.f = open(self.file_name, self.method, encoding=self.encoding)
  def __enter__(self):
    print('---这个是enter方法---')
    return self.f
  
  def __exit__(self, exc_type, exc_val, exc_tb):
    self.f.close()
    print('---这个是exit方法---')
  
with MyOpen('test.py', 'r', encoding='utf8') as f:
  print(f.read())
  print('---with的代码---')

执行结果:

5、__str__方法和__repr__方法

从上图看出,print()函数输出的内容与变量输出的内容不一样。

当我们使用print()这个内置函数之后,给它传入一个对象,那么这个时候实际上是触发了这个对象的__str__,将改方法返回的内容输出到控制台。

在交互环境中,输入对象,返回的是__repr__方法中所返回的

 

class Hero(object):
  def __init__(self, name):
    print('这个是init方法')
    self.name = name
    
  def __str__(self):
    print('___str___')
    
h = Hero('sinder')
print(h)

执行结果:返回结果类型错误,返回的是NoneType

注意:当我们定义__str__和__repr__方法时,必须设置返回值,而且返回值必须是字符串。

设置返回值之后的正确代码:

class Hero(object):
  def __init__(self, name):
    print('这个是init方法')
    self.name = name
    
  def __str__(self):
    print('___str___')
    return self.name
    
h = Hero('sinder')
print(h)

执行结果:

class Hero(object):
  def __init__(self, name):
    print('这个是init方法')
    self.name = name
  #
  # def __str__(self):
  #   print('___str___')
  #   return self.name
  
  def __repr__(self):
    print('___repr___')
    return '---repr---'


h = Hero('sinder')
print(h)

执行结果:

__str__和__repr__触发机制:

总结:

使用str函数或者print打印对象时会优先触发str方法,没定义str方法的情况下,会再去找repr方法,如果都没有,那么就会去找父类的str方法。

使用repr方法或者交互环境下输入对象,直接触发repr方法

posted @ 2019-08-18 14:33  sinder2018  阅读(47)  评论(0)    收藏  举报