Python

Python

尚硅谷Java

1.python基础

1.1深拷贝和浅拷贝

浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,

深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,

使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的错误。

浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。

深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。

import copy

if __name__ == '__main__':
    # 深拷贝
    b = copy.deepcopy(a)
    # 浅拷贝
    b = copy.copy(a)

1.2Python 中管理内存

Python 用一个私有堆内存空间来放置所有对象和数据结构,我们无法访问它。由解释器来管理它。不过使用一些核心 API,我们可以访问一些 Python 内存管理工具控制内存分配。

python的垃圾回收机制,以引用计数为主,标记清除和分代回收为辅。

image-20210818072350321
1.2.1引用计数
class Pyobj:
    def __del__(self):
        print("对象被销毁")

print("1")
obj = Pyobj()
obj = 6     # 让变量obj指向其他对象
print("2")

当obj = 6 这行被执行时,__del__方法会被执行,print(“对象被销毁”) 会先于print(“2”) 执行。

执行结果:

image-20210818072630555

引用计数的优点

  • 简单
  • 实时性高,只要引用计数为0,对象就会被销毁,内存被释放,回收内存的的时间平摊到了平时

引用计数的缺点

  • 为了维护引用计数消耗了很多资源
  • 循环引用,循环引用导致内存泄漏,例如下面的代码

下面是一个循环引用的例子

list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
1.2.2标记清除

第1阶段:标记阶段

GC会把所有活动对象打上标记,这些活动的对象就如同一个点,他们之间的引用关系构成边,最终点个边构成了一个有向图,如下图所示

image-20210818073617248

第2阶段:搜索清除阶段

从根对象(root)出发,沿着有向边遍历整个图,不可达的对象就是需要清理的垃圾对象。这个根对象就是全局对象,调用栈,寄存器。

在上图中,从root出发后,可以到达 1 2 3 4,而5, 6, 7均不能到达,其中6和7互相引用,这3个对象都会被回收。

1.2.3分代回收

分代回收建立标记清除的基础之上,是一种以空间换时间的操作方式。标记清除可以回收循环引用的垃圾,但是,回收的频次是需要控制的。

分代回收,根据内存中对象的存活时间将他们分为3代,新生的对象放入到0代,如果一个对象能在第0代的垃圾回收过程中存活下来,GC就会将其放入到1代中,如果1代里的对象在第1代的垃圾回收过程中存活下来,则会进入到2代。

1.3pass 语句

我们在写代码时,有时可能只写了函数声明而没想好函数怎么写,但为了保证语法检查的正确必须输入一些东西。在这种情况下,我们使用 pass 语句。

1.4python中的None

1.4.1None的类型

None在python中是一个特殊的对象,它表示空值,其类型为NoneType

>>> type(None)
<class 'NoneType'>
1.4.2只存在一个None

None只存在一个,python解释器启动时创建,解释器退出时销毁

>>> a = None
>>> b = None
>>> a == b
True
>>> a is b
True

由于内存None只有一个,所以a is b的结果为True

1.4.3None 的运算

None不支持任何运算,也没有内建方法,除了表示空以外,什么都做不了。

如果要判断一个对象是否为None,使用is身份运算符

>>> a = None
>>> a is None
True
1.4.4None的使用

如果一个函数,没有显式return任何数据,则默认返回None。

在判断语句中,None等价于False

>>> a = None
>>> not a
True

1.5python3 枚举

当一个变量有几种固定的取值时,通常我们喜欢将它定义为枚举类型,枚举类型用于声明一组命名的常数,使用枚举类型可以增强代买的可读性。在python2中,没有枚举这种数据类型,开发人员不得不使用其他手段来构造出近似于枚举类型的数据结构,从python3开始,python正式提供了枚举类型。

没有枚举类型之前:

class ColorCode:
    RED = 1
    BLUE = 2
    BLACK = 3

def print_color(color_code):
    if color_code == ColorCode.RED:
        print('红色')
    elif color_code == ColorCode.BLUE:
        print('蓝色')
    elif color_code == ColorCode.BLACK:
        print('黑色')
        
print_color(1)

使用枚举类型之后:

import enum

class ColorCode(enum.Enum):
    RED = 1
    BLUE = 2
    BLACK = 3

def print_color(color_code):
    if color_code == ColorCode.RED.value:
        print('红色')
    elif color_code == ColorCode.BLUE.value:
        print('蓝色')
    elif color_code == ColorCode.BLACK.value:
        print('黑色')

print_color(1)

由于继承了enum.Enum,ColorCode的类属性将无法修改,如果执行

ColorCode.RED = 4

将会引发错误

raise AttributeError('Cannot reassign members.')
AttributeError: Cannot reassign members.
1.5.1枚举值唯一

为了防止定义枚举值时出现重复的值,enum模块还提供了unique装饰器

import enum
from enum import unique

@unique
class ColorCode(enum.Enum):
    RED = 1
    BLUE = 1
    BLACK = 3

如果枚举值出现了重复的情况,由于有unique装饰器修饰,因此在执行时会报错。

1.5.2 枚举值遍历

使用for循环可以对枚举值进行遍历

import enum
from enum import unique

@unique
class ColorCode(enum.Enum):
    RED = 1
    BLUE = 2
    BLACK = 3


for color in ColorCode:
    print(color.name, color.value)

程序输出结果

RED 1
BLUE 2
BLACK 3
1.5.3 枚举值比较

枚举值之间不支持 > 和 < 操作,但支持等值比较和is身份比较

2.面向对象

面向过程(POP) 与 面向对象(OOP) :

二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的 是功能行为,以函数为最小单位,考虑怎么做。面向对象,将功能封装进对象,强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。

面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如 抽象、分类、继承、聚合、多态等。

面向对象的三大特征 封装 (Encapsulation) 继承 (Inheritance) 多态 (Polymorphism)

程序员从面向过程的执行者转化成了面向对象的指挥者

例子:(人把大象放进冰箱):

image-20210816222545205

面向对象分析方法分析问题的思路和步骤:

1.根据问题需要,选择问题所针对的现实世界中的实体。

2.从实体中寻找解决问题相关的属性和功能,这些属性和功能就形成了概念世界中的类。

3.把抽象的实体用计算机语言进行描述,形成计算机世界中类的定义。即借助某种程序语言,把类构造成计算机能够识别和处理的数据结构。

4.将类实例化成计算机世界中的对象。对象是计算机世界中解决问题的最终工具。

可以理解为:类 = 抽象概念的人;对象 = 实实在在的某个人
面向对象程序设计的重点是类的设计
类的设计,其实就是类的成员的设计

2.1封装

我们程序设计追求“高内聚,低耦合”。

高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;

低耦合 :仅对外暴露少量的方法用于使用。

通俗的说,把该隐藏的隐藏起来,该暴露 的暴露出来。这就是封装性的设计思想。

封装:提高程序的安全性
1.将数据(属性)和行为(方法)包装到类对象中。在方法内部对属性进行类对象的外部调用方法。这样,无需关心方法内部的具体实现细节,从而隔离了复杂度。
2.在python中没有专门的修饰符用于属性的私有,如果该属性不希望再类对象外部被访问,前面使用两个"-"。
class Student:
    """
    Student类
    """
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    def show(self):
        print(self.name, self.__age)


stu = Student('张三', 20)
stu.show()
# 在类的外使用name和age
print(stu.name)
# print(stu.__age)
print(dir(stu))
print(stu._Student__age)  # 在类的外部可以通过 stu._Student__age 进行访问

运行结果:

image-20210816230421473

2.2继承

继承:提高代码的复用性

python支持多继承

class A(Object):
    pass

class B(Object):
    pass

class C(A, B):
    pass

2.3多态

简单的说,多态就是“具备多种形态”。
多态:提高程序的可扩展性和可维护性。
class Animal(object):
    def eat(self):
        print('动物会吃')


class Dog(Animal):
    def eat(self):
        print('狗吃骨头')


class Cat(Animal):
    def eat(self):
        print("猫吃鱼")


class Persion:
    def eat(self):
        print('人吃五谷杂粮')
        
# 定义一个函数
def fun(obj):
    obj.eat()
    
# 开始调用函数
fun(Cat())
fun(Dog())
fun(Animal())
print('--------------------------->')
fun(Persion())

运行结果:

鸭子类型

image-20210816232514137

静态语言和动态语言关于多态的区别, 静态语言实现多态的三个必要条件:

  1. 继承
  2. 方法重写
  3. 父类引用指向子类对象

动态语言的多态崇尚"鸭子类型"当看到一只鸟走起来像鸭子、游泳起来像鸭子、收起来也像鸭子,那么这只鸟就可以被称为鸭子。在鸭子类型中,不需要关心对象是什么类型,到底是不是鸭子,只关心对象的行为。

2.4方法属于类,属性属于实例

每当你看到实例两个字时,你都应该意识到内存中存在一个实例对象,实例属性也随之存在,当实例被销毁时,属性也随之被销毁。实例属性必须依附于实例。实例方法则不然,不论实例是否存在,实例方法都客观存在,实例方法是在定义类的时候创建的,只要这个类在,那么这些实例方法就存在。

类的存在,是一种约定,约定了实例有哪些属性,约定了实例可以调用的函数(方法)。

class Stu:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print("{name} is running".format(name=self.name))

    def print(self):
        print("ok")

Stu.print(None)
s = Stu("小刚", 18)
Stu.run(s)

image-20210818081153399

通过类调用类里所定义的print方法,我这里直接传入None,在执行过程中,self就是None,即便如此,这个方法也是可以被正常执行的,因为在函数里没有使用实例属性,如果使用了呢?就会报错,self是None,不是类Stu的实例,因此没有实例属性。

Stu这个类可以直接调用方法,因为方法是属于类的,但属性属于对象,你无法通过类直接访问对象的属性,下面的写法是错误的

class Stu:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def run(self):
        print("{name} is running".format(name=self.name))

    def print(self):
        print("ok")

print(Stu.name)

原因在于,对象还没有被创建,那么name,age根本就不存在,即便是创建了,name和age属性,也是属于对象的。

image-20210818081227647

2.5类的内部管理

2.5.1一个简单的类

定义了一个简单的类

class AirVehicle(object):
    category = '飞机'

    def __init__(self, name):
        self.name = name

    def fly(self):
        print('{name} is flying'.format(name=self.name))

name是实例属性,而category是类属性,在使用过程中,很容易由于理解不清导致一些奇怪的问题

2.5.2通过类修改category
p1 = AirVehicle('歼20')
p2 = AirVehicle('歼15')

print(p1.category, p1.category)
AirVehicle.category = '战斗机'
print(p1.category, p2.category)

程序输出结果

image-20210818083624151

通过类修改category以后,对所有实例都生效了,那么通过实例进行修改是否也可以呢?

2.5.3通过实例修改category
p1 = AirVehicle('歼20')
p2 = AirVehicle('歼15')

print(p1.category, p1.category)
p1.category = '战斗机'
print(p1.category, p2.category)

程序输出结果

image-20210818083645682

通过实例p1修改category属性,只对p1生效了,p2的category属性仍然是飞机,想要搞清楚这种差别,就需要探究python是如何存储实例属性和类属性的

2.5.4python如何存储实例属性

python中,实例属性存储在一个字典(dict)中,对于属性的操作,都是在操作这个字典

p1 = AirVehicle('歼20')
print(p1.__dict__)

程序输出结果

image-20210818083711909

可以直接操作这个字典

p1 = AirVehicle('歼20')
p1.__dict__['speed'] = '2马赫'
print(p1.speed)   # 2马赫
2.5.5python如何存储类属性

同样是存储在字典中,只是这个字典是类的字典

print(AirVehicle.__dict__)

程序输出结果

{'__module__': '__main__', 'category': '飞机', '__init__': <function AirVehicle.__init__ at 0x103eefd90>, 
'fly': <function AirVehicle.fly at 0x103f0d268>, '__dict__': <attribute '__dict__' of 'AirVehicle' objects>, '__weakref__': <attribute '__weakref__' of 'AirVehicle' objects>, '__doc__': None}

这次输出的信息比较多,我们只关注category和fly,类的属性和方法存在在类的__dict__之中

4.多线程

4.1并行和并发

并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行。
并行:单位时间内,多个处理器或多核处理器同时处理多个任务,是真正意义上的“同时进行”。
串行:有n个任务,由一个线程按顺序执行。由于任务、方法都在一个线程执行所以不存在线程不安全情况,也就不存在临界区的问题。

做一个形象的比喻:

并发 = 两个队列和一台咖啡机。

并行 = 两个队列和两台咖啡机。

串行 = 一个队列和一台咖啡机。

class Logging():
    lock = threading.Lock()
    
    ......
    
    def file_logger_out(self, hex_str, log_type):
        try:
            # 这是使用同一个锁,当加锁之后,其他使用同一个锁的线程,需要等当前线程释放锁,才能继续
            Logging.lock.acquire()
            logger = self.logger['file'][log_type]['logger']
            logger.info(hex_str)
            Logging.lock.release()
        except Exception as e:
            print_with_time_stamp(e)
            print_with_time_stamp(traceback.print_exc())

5.算法分析

5.1哈希表

Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。

"""
This module contains a code example related to
Think Python, 2nd Edition
by Allen Downey
http://thinkpython2.com
Copyright 2015 Allen Downey
License: http://creativecommons.org/licenses/by/4.0/
"""

from __future__ import print_function, division


class LinearMap:
    """A simple implementation of a map using a list of tuples
    where each tuple is a key-value pair."""

    def __init__(self):
        self.items = []

    def add(self, k, v):
        """Adds a new item that maps from key (k) to value (v).
        Assumes that they keys are unique."""
        self.items.append((k, v))

    def get(self, k):
        """Looks up the key (k) and returns the corresponding value,
        or raises KeyError if the key is not found."""
        for key, val in self.items:
            if key == k:
                return val
        raise KeyError


class BetterMap:
    """A faster implementation of a map using a list of LinearMaps
    and the built-in function hash() to determine which LinearMap
    to put each key into."""

    def __init__(self, n=100):
        """Appends (n) LinearMaps onto (self)."""
        self.maps = []
        for i in range(n):
            self.maps.append(LinearMap())

    def find_map(self, k):
        """Finds the right LinearMap for key (k)."""
        index = hash(k) % len(self.maps)
        return self.maps[index]

    def add(self, k, v):
        """Adds a new item to the appropriate LinearMap for key (k)."""
        m = self.find_map(k)
        m.add(k, v)

    def get(self, k):
        """Finds the right LinearMap for key (k) and looks up (k) in it."""
        m = self.find_map(k)
        return m.get(k)


class HashMap:
    """An implementation of a hashtable using a BetterMap
    that grows so that the number of items never exceeds the number
    of LinearMaps.
    The amortized cost of add should be O(1) provided that the
    implementation of sum in resize is linear."""

    def __init__(self):
        """Starts with 2 LinearMaps and 0 items."""
        self.maps = BetterMap(2)
        self.num = 0

    def get(self, k):
        """Looks up the key (k) and returns the corresponding value,
        or raises KeyError if the key is not found."""
        return self.maps.get(k)

    def add(self, k, v):
        """Resize the map if necessary and adds the new item."""
        if self.num == len(self.maps.maps):
            self.resize()

        self.maps.add(k, v)
        self.num += 1

    def resize(self):
        """Makes a new map, twice as big, and rehashes the items."""
        new_map = BetterMap(self.num * 2)

        for m in self.maps.maps:
            for k, v in m.items:
                new_map.add(k, v)

        self.maps = new_map


def main():
    import string

    m = HashMap()
    s = string.ascii_lowercase

    for k, v in enumerate(s):
        m.add(k, v)

    for k in range(len(s)):
        print(k, m.get(k))


if __name__ == '__main__':
    main()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值