Python新式类 单例模式与作用域(四)

1 新式类与旧式类

新式类拥有经典类的全部特性之外,还有一些新的特性,比如 __init__发生变化,新增了静态方法__new__,python3目前都采用新式类,新式类是广度优先,旧式类是深度优先

#新式类
class C(object):
    pass
#经典类
class B:
    pass
(1)内置的object对象

1. __new__,__init__方法
这两个方法是用来创建object的子类对象,静态方法__new__()用来创建类的实例,然后再调用
__init__()来初始化实例。

2. __delattr__, __getattribute__, __setattr__方法
对象使用这些方法来处理属性的访问

3. __hash__, __repr__, __str__方法
print(someobj)会调用someobj.__str__(), 如果__str__没有定义,则会调用someobj.__repr__(),

__str__()和__repr__()的区别:
默认的实现是没有任何作用的
__repr__的目标是对象信息唯一性
__str__的目标是对象信息的可读性
容器对象的__str__一般使用的是对象元素的__repr__
如果重新定义了__repr__,而没有定义__str__,则默认调用__str__时,调用的是__repr__
也就是说好的编程习惯是每一个类都需要重写一个__repr__方法,用于提供对象的可读信息,
而重写__str__方法是可选的。实现__str__方法,一般是需要更加好看的打印效果,比如你要制作
一个报表的时候等。
(2)类的方法

静态方法

静态方法可以被类或者实例调用,它没有常规方法的行为(比如绑定,非绑定,默认的第一个self参数),当有一
堆函数仅仅是为了一个类写的时候,采用静态方法声明在类的内部,可以提供行为上的一致性。
使用装饰符@staticmethod进行创建 

类方法

也是可以通过类和它的实例进行调用,不过它是有默认第一个参数,叫做是类对象,一般被
命名为cls,当然你也可以命名为其它名字,这样就你可以调用类对象的一些操作,
代码如下,使用装饰符@classmethod创建:

新式类(new-style-class)

__init__方法: 类的初始化方法

__new__静态方法

新式类都有一个__new__的静态方法,它的原型是object.__new__(cls[, ...])

cls是一个类对象,当你调用C(*args, **kargs)来创建一个类C的实例时,python的内部调用是

C.__new__(C, *args, **kargs),然后返回值是类C的实例c,在确认

c是C的实例后,python再调用C.__init__(c, *args, **kargs)来初始化实例c。

所以调用一个实例c = C(2),实际执行的代码为:

c = C.__new__(C, 2)
if isinstance(c, C):
    C.__init__(c, 23)#__init__第一个参数要为实例对象

class Singleton(object):
    _singletons = {}
    def __new__(cls):
        if not cls._singletons.has_key(cls):            #若还没有任何实例
            cls._singletons[cls] = object.__new__(cls)  #生成一个实例
        return cls._singletons[cls]                     #返回这个实例
    a = Singleton()
    b = Singleton()
    id(a)  #35966666
    id(b)  #35966666
 #注:单例模式 ,两个实例指向同一个内存地址 
(3)新式类实例

新式类的实例也具有新的特性。比如它拥有Property功能,该功能会对属性的访问方式产生影响;还有__slots__新属性,该属性会对生成子类实例产生影响;还添加了一个新的方法__getattribute__,比原有的__getattr__更加通用。

__slots__属性

通常每一个实例x都会有一个__dict__属性,用来记录实例中所有的属性和方法,也是通过这个字典,

可以让实例绑定任意的属性。而__slots__属性作用就是,当类C有比较少的变量,而且拥有__slots__属性时,

类C的实例 就没有__dict__属性,而是把变量的值存在一个固定的地方。如果试图访问一个__slots__中没有

的属性,实例就会报错。这样操作有什么好处呢?__slots__属性虽然令实例失去了绑定任意属性的便利,

但是因为每一个实例没有__dict__属性,却能有效节省每一个实例的内存消耗,有利于生成小而精

干的实例。

定义__slots__属性

class A(object):
    def __init__(self):
        self.x = 1
        self.y = 2
        __slots__ = (‘x‘,‘y‘)
a = A()
a.z = 3
a.u = 4  #都会报错,不能对实例新增属性,__dict__字典集没有任何改变

class A(object):
    def __init__(self):
        self.x = 1
        self.y = 2
a = A()
a.x = 3  #不会报错,在__dict__字典字典集会新增‘x‘属性

使用时__slots__时需要注意的几点:

1.  当一个类的父类没有定义__slots__属性,父类中的__dict__属性总是可以访问到的,所以只在子
类中定义__slots__属性,而不在父类中定义是没有意义的。
2. 如果定义了__slots__属性,还是想在之后添加新的变量,就需要把‘__dict__‘字符串添加到__slots__的
元组里。
3. 定义了__slots__属性,还会消失的一个属性是__weakref__,这样就不支持实例的weak reference,
如果还是想用这个功能,同样,可以把‘__weakref__‘字符串添加到元组里。
4. __slots__功能是通过descriptor实现的,会为每一个变量创建一个descriptor。
5. __slots__的功能只影响定义它的类,因此,子类需要重新定义__slots__才能有它的功能。
__getattribute__方法

对新式类的实例来说,所有属性和方法的访问操作都是通过__getattribute__完成,这是由object基类实现的。如果有特殊的要求,可以重载__getattribute__方法.

2 __init____new__区别

  1. __new__是一个静态方法,而__init__是一个实例方法.
  2. __new__方法会返回一个创建的实例,而__init__什么都不返回.
  3. 只有在__new__返回一个cls的实例时后面的__init__才能被调用.
  4. 当创建一个新实例时调用__new__,初始化一个实例时用__init__.

__metaclass__是创建类时起作用.所以我们可以分别使用__metaclass__,__new____init__来分别在类创建,实例创建和实例初始化的时候做一些操作

3 单例模式

? 单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源,系统中存在多个该类的实例对象,而这样会严重浪费内存资源 如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。

__new__()__init__()之前被调用,用于生成实例对象。利用这个方法和类的属性的特点可以实现设计模式的单例模式。单例模式是指创建唯一对象,单例模式设计的类只能实例一次

一般python中可以采用以下方法实现单例模式

  • 使用模块(import导入)
  • 使用 __new__
  • 使用装饰器(decorator)
  • 使用元类(metaclass)

(1)使用__new__方法

class Singleton(object):
    def __new__(cls, *args, **kw):
        if not hasattr(cls, ‘_instance‘): #如果该类是否含有实例
            orig = super(Singleton, cls)
            cls._instance = orig.__new__(cls, *args, **kw)
        return cls._instance

class MyClass(Singleton):
    a = 1

#如果 cls._instance 为 None 则创建实例,否则直接返回 cls._instance。

(2)共享属性(使用metaclass)

元类(metaclass)可以控制类的创建过程,它主要做三件事:

  • 拦截类的创建
  • 修改类的定义
  • 返回修改后的类

创建实例时把所有实例的__dict__指向同一个字典,这样它们具有相同的属性和方法.

class Borg(object):
    _state = {}
    def __new__(cls, *args, **kw):
        ob = super(Borg, cls).__new__(cls, *args, **kw)
        ob.__dict__ = cls._state
        return ob

class MyClass2(Borg):
    a = 1

(3) 装饰器版本

def singleton(cls):
    instances = {}
    def getinstance(*args, **kw):
        if cls not in instances:
            instances[cls] = cls(*args, **kw)
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

(4) import 方法(使用模块)

作为python的模块是天然的单例模式

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass
my_singleton = My_Singleton()

# to use
from mysingleton import my_singleton #将类进行导入使用
my_singleton.foo()

4 Python作用域

python的作用域分全局和局部,python变量的作用域分为以下四类:

L(local) 局部作用域

E(Enclosing) 闭包函数外的函数中

G(Global) 全局作用域

B(Built-in) 内建作用域

(1) 局部作用域

在了解局部作用域之前,先了解下块级作用域的概念

#块级作用域
if 1 == 1:
    name = "lol"
print(name) 

for i in range(10):
    age = i
print(age)

#输出:
C:/Users/L/PycharmProjects/s14/preview/Day8/作用域/test.py
lol
9

可以发现python代码运行ok,为什么外部可以调用内部的变量呢? 因为在python中没有块级作用域的概念,代码块里的变量,外部可以调用,所以可运行成功, 也就是说,类似条件判断(if…..else)、循环语句(for x in data)、异常捕捉(try…catch)等的变量是可以全局使用的 .但是不区分作用域明显是不行的,因此python引入局部作用域

#局部作用域
def  func():
    name = "lol"
func()  #调用函数
print(name)
#输出
Traceback (most recent call last):
  File "C:/Users/L/PycharmProjects/s14/preview/Day8/作用域/test.py", line 23, in <module>
    print(name)
NameError: name ‘name‘ is not defined

即使执行了一下函数,name的作用域也只是在函数内部,外部依然无法进行调用 ,因此函数可以产生局部作用域,在python中模块(module),类(class)、函数(def、lambda)会产生新的作用域 .

(2) 作用域链

#作用域链
name = "张三"
def func1():
    name = "李四"
    def func2():
        name = "王五"
        print(name)
    func2()
func1()
#输出
"王五"

func1()调用函数执行变量赋值操作,当调用func2()时 , print(name)中name属性会先从局部作用域开始寻找,由内至外,最先找到的当然是 ‘王五‘.Python中有作用域链,变量会由内到外找,先去自己作用域去找,自己没有再去上级去找,直到找不到报错 .

(3) 全局作用域与内建作用域

x = int(2.9)  # 内建作用域
num = 0  # 全局作用域
def outer():
    num2 = 1  # 闭包函数外的函数中
    def inner():
        num3 = 2  # 局部作用域

(4) global关键字

全局变量是指在函数外的变量,可以在程序全局使用,局部变量是指定义在函数内的变量,只能在函数内被声明使用若内部作用域的想要修改外部作用域的变量,就要使用global关键字

a = 100

def demo():
    global a
    a = 123
    print(a)
demo()
print(a)

#运行结果是
123
123

本来运行结果应该是123,100,但是因为global声明a为全局作用域,因此在执行赋值后的a指向123,因此两次打印的都是123.

总结下:

? Python 中,一个变量的作用域总是由在代码中被赋值的地方所决定的。当 Python 遇到一个变量的话他会按照这样的顺序进行搜索:本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)

原文地址:https://www.cnblogs.com/why957/p/9196262.html

时间: 2024-08-12 08:35:37

Python新式类 单例模式与作用域(四)的相关文章

Python新式类和经典类的区别

@Python新式类和经典类的区别 class ClassicClass(): pass class NewStyleClass(object): pass x1 = ClassicClass() x2 = NewStyleClass() print x1.__class__, type(x1) print x2.__class__, type(x2) 输出结果: __main__.ClassicClass <type 'instance'> <class '__main__.NewSt

【Python】Python 新式类介绍

本文转载自:kaka_ace's blog 我们使用 Python 开发时, 会遇到 class A 和 class A(object) 的写法, 这在 Python2 里是有概念上和功能上的区别, 即经典类(旧式类)与新式类的区别, 英文上分别描述为 old-style(classic-style) 与 new-style. 通过搜索, 先查阅了三个资料链接: 官方文档 stackoverflow 解答 Python Types and Objects 根据 stackoverflow 答案引

Python新式类与经典类的区别

1.新式类与经典类 在Python 2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于“新式类”,都会获得所有“新式类”的特性:反之,即不由任意内置类型派生出的类,则称之为“经典类”. “新式类”和“经典类”的区分在Python 3之后就已经不存在,在Python 3.x之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类”. 官方文档 https://www.python.org/doc/new

Python新式类

1.Python  2.x版本默认旧式类,通过继承object为新式类 2.新式类广度搜索继承,旧式类深度搜索继承 3.__new__方法 4.统一了类x.__class__和类型(type(x)) 5.增加了__slots__,去除了__dict__属性(用来记录实例中属性和方法,让实例绑定任意属性的字典),只有slots中有的属性才能被使用,不能动态增加属性. 6.增加了__getattribute__方法,该方法在实例访问属性时被调用,无论有没有对应的属性,有返回 7.新增很多管理内建函数

Python新式类继承的C3算法

在Python的新式类中,方法解析顺序并非是广度优先的算法,而是采用C3算法,只是在某些情况下,C3算法的结果恰巧符合广度优先算法的结果. 可以通过代码来验证下: class NewStyleClassA(object): var = 'New Style Class A' class NewStyleClassB(NewStyleClassA): pass class NewStyleClassC(NewStyleClassA): var = 'New Style Class C' class

python 新式类和旧式类

新式类和旧式类 python的新式类是2.2版本引进来的,我们可以将之前的类叫做经典类或者旧类. 为什么要在2.2中引进new style class呢?官方给的解释是: 为了统一类(class)和类型(type). 在2.2之前,比如2.1版本中,类和类型是不同的,如a是ClassA的一个实例,那么a.__class__返回 ' class    __main__.ClassA' ,type(a)返回总是<type 'instance'>.而引入新类后,比如ClassB是个新类,b是Clas

Python 新式类与经典类

新式类,经典类 查询匹配 广度查询 横着对每个类进行查询 深度查询 无视平级类,直接寻找下级类 #python 3.0 #新式类 广度查询 #经典类 广度查询 #python 2.0 #新式类 广度查询 #经典类 深度查询

python新式类与旧式类

python2.X是经典类[旧式类]: 是以深度优先[] 但是在Python2.x中,默认都是经典类,只有显式继承了object才是新式类,即:class Person(object):pass 新式类写法class Person():pass 经典类写法class Person:pass 经典类写法 他们最明显的区别在于继承搜索的顺序发生了改变,即经典类多继承搜索顺序(深度优先):先深入继承树左侧查找,然后再返回,开始查找右侧,如图所示: 1           A 2-1 B       

python 新式类和经典类(了解)

在python2中,有新式类和经典类和经典类的区别: 新式类 继承自object或object的子类,这样的类就叫新式类. class A(object): pass print(A.__bases__) 结果: (<class 'object'>,) 经典类 没有继承object或object的子类. >>> class A: ... pass ... >>> A.__bases__ () >>> 而在python3中不再有这种区别,所有