python:高级(一)

自己以前整理的笔记,不太完整,后续会不断更新。。。。


一、GIL全局解释器锁

单进程单线程程序开多个、单进程多线程、多进程中多线程是假的,因为python解释器(Cpython)中存在GIL,每个线程在执行的过程都需要先获取GIL,它保证同一时间CPU只能执行一个线程。

而单进程单线程程序开多个、多进程可最大化使用多核CPU性能。

只有Cpython解释器中存在GIL

多线程爬取比单线程性能有提升,因为遇到IO阻塞会自动释放GIL锁

计算密集型:充分调用CPU的计算性能,大量运算------>进程

IO密集型:读写操作频繁,如:网络传输,文件的读写--------->线程/协程

解决GIL问题的方法:

  • 更换python解释器
  • 在子线程中引入其他编程语言

二、 深、浅拷贝

import copy

深拷贝:copy.deepcopy()

浅拷贝:copy.copy()

  • 浅拷贝是对于一个对象的顶层拷贝
  • 深拷贝是对于一个对象的深层拷贝,所有数据均进行拷贝
  • 如果浅拷贝拷贝的是元组,它不会进行浅拷贝,仅仅是指向元组
  • 如果用copy.copy()、copy.deepcopy()对一个全部都是不可变类型的数据进行拷贝,那么他们都是拷贝引用
  • 如果拷贝的是一个不可变类型的数据,内部包含可变类型数据,那么copy.deepcopy()是深层拷贝,而copy.copy()还是拷贝引用

切片拷贝

切片是浅拷贝

字典拷贝
字典中的value值都是引用,那么当浅拷贝时拷贝的是引用,深拷贝依然是全复制数据

import copy


def main():
    dic1=dict(name='zhangshan',age=18,temp=[1,2])

    co1=copy.copy(dic1)  # 浅拷贝,拷贝的引用
    dic1['temp'].append(3)

    print(id(dic1))
    print(id(co1))
    print(dic1)
    print(co1)

    co2=copy.deepcopy(dic1)  # 深拷贝,拷贝整个数据
    dic1['temp'].append(3)

    print(id(dic1))
    print(id(co2))
    print(dic1)
    print(co2)


if __name__ == '__main__':
    main()

深拷贝的应用:

在获取源数据后,要进行操作,为不污染源数据,可采用深拷贝而不影响源数据

三、 import

变量类型:

_xx:单前置下划线变量,当作为模块导入其他程序中时,该变量不可访问

__xx:双前置下划线变量,私有属性或方法,子类无法继承和使用

__xx__:前后双下划线,魔法方法或属性

xx_:单后置下划线,用于避免与python关键词冲突

import aa as AA:则以aa作为模块名无法使用

注意变量名不要与模块名重名

模块导入路径:

可在sys.path中查看,返回一个搜索路径列表,最先搜索当前路径,再搜索sys.path中的其他路径

若没有要使用模块的路径,则可以在sys.path列表中添加

sys.path.append('/home/itcast/xxx')
sys.path.insert(0, '/home/itcast/xxx')

在程序中import xx如果出现多次,则只有第一次的导入有效

程序中导入某一模块后,一直运行,若修改模块中的内容,那么不会影响到运行程序中的模块

这时可以使用

import xx
....

from imp import reload
reload(xx)

多模块开发注意事项

多个模块共享数据时的坑 ????????????????

from module import data

从其他模块导入的数据data,相当于在本程序内创建了一个全局变量data,其值与模块变量值相同。但如果通过赋值的方式修改该数据则只能修改本地的全局变量,被导入模块中的数据不会变。

要同步改变被导入模块的数据,可以采用以下方法:

  • 通过数据内置的方法,如列表的append\extend等
  • 通过通过module.data的方式调用数据

四、super()调用顺序

多重继承嵌套多继承的情况下,super()保证所有父类只调用一次

super().__init__相对于 类名.__init__,在单继承上用法基本无差,但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次

使用类名.__mro__可以查看当前类对父类的继承顺序,返回值是一个类排序元组

更多见:super()对多继承的影响

五、实例方法、静态方法和类方法

方法包括:实例方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同

  • 实例方法:由对象调用;至少一个self参数;执行实例方法时,自动将调用该方法的对象赋值给self;
  • 类方法:由类调用; 至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;
  • 静态方法:由类调用;无默认参数;

六、property属性

一种用起来像是使用的实例属性一样的特殊属性,可以对应于某个方法

  • 定义时,在实例方法的基础上添加 @property 装饰器;并且仅有一个self参数
  • 调用时,无需括号
  • 一般要有返回值

创建property属性的两种方法:

  • 装饰器 即在方法上应用装饰器
  • 类属性 即在类中定义值为property对象的类属性

1.装饰器方式

在类的实例方法上应用@property装饰器

Python中的类有经典类新式类新式类的属性比经典类的属性丰富

经典类,具有一种@property装饰器

@property
    def price(self):
        return "laowang"

新式类,具有三种@property装饰器

由于新式类中具有三种访问方式,我们可以根据它们几个属性的访问特点,分别将三个方法定义为对同一个属性:获取、修改、删除

# *-* coding:utf-8 *-*

# 三种装饰器,只能在新式类中使用
# 经典类中只能使用@property


class Goods:
    def __init__(self, price):
        self.original_price = price
        self.discount = 0.8

    @property
    def price(self):
        new_price = self.original_price * self.discount
        return new_price

    @price.setter  # 方法名必须与@property下的方法名相同
    def p1(self, value):  # 这个方法名可以不同,但是在实例访问时要对应
        self.original_price = value

    @price.deleter  # 方法名必须与@property下的方法名相同
    def p2(self):
        del self.original_price


apple = Goods(10)
print(apple.price)

apple.p1 = 20  # 执行该语句后调用p1方法
print(apple.price)

del apple.p2  # 执行该语句后调用p2方法
# print(apple.price)  # 删除对象属性后报错

2.类属性方式

当使用类属性的方式创建property属性时,经典类新式类无区别

class Foo(object):
    def get_bar(self):
        print("getter...")
        return 'laowang'

    def set_bar(self, value): 
        """必须两个参数"""
        print("setter...")
        return 'set value' + value

    def del_bar(self):
        print("deleter...")
        return 'laowang'

    BAR = property(get_bar, set_bar, del_bar, "description...")

obj = Foo()

obj.BAR  # 自动调用第一个参数中定义的方法:get_bar
obj.BAR = "alex"  # 自动调用第二个参数中定义的方法:set_bar方法,并将“alex”当作参数传入
desc = Foo.BAR.__doc__  # 自动获取第四个参数中设置的值:description...
print(desc)
del obj.BAR  # 自动调用第三个参数中定义的方法:del_bar方法

property方法中有个四个参数

  • 第一个参数是方法名,调用 对象.属性 时自动触发执行方法
  • 第二个参数是方法名,调用 对象.属性 = XXX 时自动触发执行方法
  • 第三个参数是方法名,调用 del 对象.属性 时自动触发执行方法
  • 第四个参数是字符串,调用 对象.属性.doc ,此参数是该属性的描述信息

property的应用:

使用公有方法实现对私有属性的访问和设置

七、魔法属性及方法

__doc__属性

表示类的描述信息

class Foo:
    """ 描述类信息,这是用于看片的神奇 """
    def func(self):
        pass

print(Foo.__doc__)

__module__和__class__属性

  • __module__ 表示当前操作的对象在哪个模块
  • __class__ 表示当前操作的对象的类是什么,如果是其他模块的类也会显示模块名

__call__方法

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

class Foo:
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')


obj = Foo()  # 执行 __init__
obj()  # 执行 __call__

__dict__属性

类或对象中的所有属性,以字典的格式输出

__getitem__、__setitem__、__delitem__方法

用于索引操作,如字典。以上分别表示获取、设置、删除数据

class Foo(object):

    def __getitem__(self, key):
        print('__getitem__', key)

    def __setitem__(self, key, value):
        print('__setitem__', key, value)

    def __delitem__(self, key):
        print('__delitem__', key)


obj = Foo()

result = obj['k1']      # 自动触发执行 __getitem__
obj['k2'] = 'laowang'   # 自动触发执行 __setitem__
del obj['k1']           # 自动触发执行 __delitem__

__getslice__、__setslice__、__delslice__方法

该三个方法用于分片操作,如:列表

class Foo(object):

    def __getslice__(self, i, j):
        print('__getslice__', i, j)

    def __setslice__(self, i, j, sequence):
        print('__setslice__', i, j)

    def __delslice__(self, i, j):
        print('__delslice__', i, j)

obj = Foo()

obj[-1:1]                   # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44]    # 自动触发执行 __setslice__
del obj[0:2]                # 自动触发执行 __delslice__

八、with与上下文管理器

with语句

对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源

Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,因为系统允许你打开的最大文件数量是有限的

对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections",因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。

普通打开文件方式:

def m1():
    f = open("output.txt", "w")
    f.write("python之禅")
    f.close()

这样写有一个潜在的问题,如果在调用 write 的过程中,出现了异常进而导致后续代码无法继续执行,close 方法无法被正常调用,因此资源就会一直被该程序占用者释放

进阶打开文件方式:

def m2():
    f = open("output.txt", "w")
    try:
        f.write("python之禅")
    except IOError:
        print("oops error")
    except Exception as e:
        pass
    finally:
        f.close()

将close放在finally中,这样无论是否发生异常,都会执行该语句

高级打开文件方式:

def m3():
    with open("output.txt", "r") as f:
        f.write("Python之禅")

open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法,无论读写文件是否成功

但如果open语句发生错误,则直接退出with语句

上下文管理器

任何实现了 __enter__() 和 __exit__() 方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 with 关键字。显然,文件(file)对象也实现了上下文管理器

原理:紧跟with后面的语句被求值后,返回对象的__enter__()方法被调用,这个方法的返回值将被赋值给as后面的变量。当with后面的代码块全部被执行完之后,将调用前面返回对象的__exit__()方法。

class Sample:
    def __enter__(self):
        print("In __enter__()")
        return "Foo"

    def __exit__(self, type, value, trace):
        print("In __exit__()")


def get_sample():
    return Sample()


with get_sample() as sample:
    print("sample:", sample)

# In __enter__()
# sample: Foo
# In __exit__()
class File():

    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        print("entering")
        self.f = open(self.filename, self.mode)
        return self.f

    def __exit__(self, *args):
        print("will exit")
        self.f.close()

with File('out.txt', 'w') as f:
    print("writing")
    f.write('hello, python')

__enter__() 方法返回资源对象,赋值给f

__exit__() 方法处理一些清除工作,当with下的处理语句发生异常或执行完后自动执行__exit__() 方法

自定义上下文管理器

# 自定义上下文管理器:DummyResource

# 注意with代码的执行顺序:
# 1 执行DummyResource对象--对象的初始化,执行__enter__()方法,(可以用as接收__enter__的返回值)
# 2 执行with下的语句
# 3 执行DummyResource中的__exit__()方法


class DummyResource:
    def __init__(self, tag):
        self.tag = tag
        print('Resource [%s]' % tag)

    def __enter__(self):
        print('[Enter %s]: Allocate resource.' % self.tag)
        return self   # 可以返回不同的对象

    def __exit__(self, exc_type, exc_value, exc_tb):  # 三个参数用于对异常的判断
        print('[Exit %s]: Free resource.' % self.tag)
        if exc_tb is None:
            print('[Exit %s]: Exited without exception.' % self.tag)
        else:
            print('[Exit %s]: Exited with exception raised.' % self.tag)


with DummyResource('Normal'):
    print('[with-body] Run without exceptions.')

另外一种实现方式

Python 还提供了一个 contextmanager 的装饰器,更进一步简化了上下文管理器的实现方式。通过 yield 将函数分割成两部分,yield 之前的语句在 __enter__ 方法中执行,yield 之后的语句在 __exit__ 方法中执行。紧跟在 yield 后面的值是函数的返回值

from contextlib import contextmanager

@contextmanager
def my_open(path, mode):
    f = open(path, mode)
    yield f
    f.close()

with my_open('out.txt', 'w') as f:
    f.write("hello , the simplest context manager")

九 、元类

python中类同样也是一种对象,也能

  1. 你可以将它赋值给一个变量
  2. 你可以拷贝它
  3. 你可以为它增加属性
  4. 你可以将它作为函数参数进行传递

使用type创建类

type除了能判断数据类型,还有一种完全不同的功能,动态的创建类

type可以接受一个类的描述作为参数,然后返回一个类

type(类名, 由父类名称组成的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

def echo_bar(self):  # 定义了一个普通的函数
    print(self.bar)

@staticmethod
def test_static():
    print("static method ....")

Test2 = type("Test2", (Test1,), {'bar': True,'echo_bar':echo_bar,'test_static':test_static}) # 定了一个Test2类
# 添加的属性是类属性,并不是实例属性
# type的第2个参数,元组中是父类的名字,而不是字符串
# 添加一个实例方法echo_bar
# 添加一个静态方法test_static

在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的

元类就是用来创建这些类(对象)的,元类就是类的类

函数type实际上是一个元类,type就是Python在背后用来创建所有类的元类

Python中所有的东西,它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type

type就是Python的内建元类,当然了,你也可以创建自己的元类

自定义元类

元类的主要目的就是为了当创建类时能够自动地改变类

元类的创建可以通过继承type元类来实现

当然也可以自定义:

python2中在定义一个类的时候为其添加__metaclass__属性

class Foo(object):
    __metaclass__ = something…

Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,继续在父类中寻找,如果任何父类中都没又找到,就会用内建的type来创建这个类

对应的在 python3中使用的是metaclass=元类名

class Foo(object, metaclass=元类名):
    bar = 'bip'

上述语句的意思就是,通过xxx元类来创建类对象Foo

元类可以接收当前类的类名,父类信息,属性信息,其形式与type()创建类时的参数形式类似

当出现class Foo()时,即说明创建了元类xxx的“实例对象”,此时,可以通过重写元类中的__new__方法来实现创建类Foo的自定义,同时也可接收上述的Foo类的相关信息

十、ORM

ORM 是 python编程语言后端web框架 Django的核心思想,“Object Relational Mapping”,即对象-关系映射,简称ORM

要实现的功能是:创建一个实例对象,用创建它的类名当做数据表名,用创建它的类属性对应数据表的字段,当对这个实例对象操作时,能够对应MySQL语句

猜你喜欢

转载自blog.csdn.net/weixin_34217773/article/details/88269323