Python从入门到放弃(1)

Posted by 高庆东 on November 3, 2019

面向对象

一定要面向对象编程
1考虑任务需求,将任务划分抽象成大中小类,并确定从属关系,确定类,方法,属性。继承关系,嵌套关系
2从大类开始创建和简单的小的需要确定的方法。
3从实例化的角度同时考虑类的功能和结构等。可以思考继承的方法
4每一种方法都只实现一个简单的功能
5 在面向对象中继承的观念非常重要 其中super 至关重要
super的使用
函数继承父类中的方法

```
class A(object):  
    def __init__(self):    
        print("__init__()")  
    def go(self):  
        print("go")  
clss B(A):  
    def __init(self):  
        print("__init__()")  
    def go(self):    
        super(B,self).go()  
        print("go")  
```

Python系统运行 与C

Python 可以导入c中的各种库,通过 from ctypes import * 其中 so=cdll.LoadLibrary('./*.so')c语言的库文件都是以.so结尾。库文件相当于一个函数,静态库与程序打包成一个整体,动态库是将地址打包到程序中。
在服务器端运行python文件时,引用模块时需要将文件的路径加载到系统中

import sys
sys.path.append('/data/eddie/流量监控/Metis')

py与pyc文件 其实是一样的东西,在执行的时候如果两个文件的修改时间一致则执行pyc文件否则执行py文件pyc文件具有更快的执行速度,并不是所有的py文件都需要生成pyc文件。
在py程序中执行命令行命令需要使用os.system('命令')
使用c程序需要执行编译将.c文件编译成.o文件 之后才能运行
当我们需要使用cython的方式写代码时,可以大大提高运行速度,可以直接将代码编译成C代码进行运行。当需要生成使用import方式调用的软件包时,可以使用cythob的方式进行写代码,通过setup安装生成python包
首先创建一个文件,该文件是.py文件,也就是需要加速得到代码,使用setup工具将代码变成import包

vim hello.py
在其中写测试代码
print("hello")
vim setup.py
在其中写
from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("hello.py")
)
保存后,命令行进入setup.py所在目录,并输入python setup.py build_ext --inplace
在函数调用时使用
import hello

在有些情况下我们需要加快速度,可以使用C和python混编,其中在python中的c和和python类似定义的函数使用cdef,同时变量需要有类型,函数返回值需要有
类型 具体的一些细节可以看这个cython关键字

Python中的装饰器内在装饰器

@装饰器 作用是为已存在的函数添加额外的功能,函数执行前先执行装饰器内容。在Python 中有三个特殊的装饰器 @staticmethod,@classmethod,@property 这三个装饰器是python自带的。

@property:将类中的方法像引用字段那样引用

class TestClass:
    def __init__(self, name):
        self.name = name
    @property
    def sayHello(self):
        print "hello", self.name
cls = TestClass("felix")
print(cls.name)
cls.sayHello

@staticmethod:将类中的方法装饰为静态方法,即类不需要创建实例的情况下,可以通过类名直接引用。到达将函数功能与实例解绑的效果。当看到该类时第一想到可> 通过类名直接调用。同时可以看作是独立的函数使用。

class Test(object):
    def __init__(self,name):
        self.name=name
        print(name)
    @staticmethod
    def test1(self):
        print("test1")
Test.test1()

python与内存

Python的内存机制呈现金字塔形状,-1,-2层主要有操作系统进行操作;2.第0层是C中的malloc,free
等内存分配和释放函数进行操作;3.第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实
现,当对象小于256K时有该层直接分配内存;4.第3层是最上层,也就是我们对Python对象的直接操作;
Python在运行期间会大量地执行malloc和free的操作,频繁地在用户态和核心态之间进行切换,这将严
影响Python的执行效率。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块
内存的申请和释放 Python内部默认的小块内存与大块内存的分界点定在256个字节,当申请的内存小于
256字节时,PyObject_Malloc会在内存池中申请内存;当申请的内存大于256字节时,PyObject_Malloc
的行为将蜕化为malloc的行为。当然,通过修改Python源代码,我们可以改变这个默认值,从而改变Python
的默认内存管理行为。
说到内存就不得不说深浅拷贝和赋值,首先赋值是把对象的内存地方复制一份,然后给其他变量,根据这个变量来
修改对象对应也会影响其赋值前的对象。

a=[1,2,3,[4,5]]
b=a
print(id(a)==id(b))
#结果:True

拷贝的意思就是拷贝对象,会生成两份对象也就是占两份内存,但是浅的意思是只拷贝第一层对象。

import copy
a=[1,2,3,[4,5]]
b=copy.copy(a)
print(id(a)==id(b))
#结果:False
print(id(a[-1]==id(b[-1])))
#结果:True

深拷贝就是对对象整体复制,内存会确实占真双份

import copy
a=[1,2,3,[4,5]]
b=copy.deepcopy(a)
print(id(a)==id(b))
print(id(a[-1])==id(b[-1]))
#结果:False   Fasle

垃圾回收

在回收机制中有个引用计数器,用来记录对象的引用次数,在python中对象是实际占用内存的东西,每种操作
都会使计数器放生或增或减的效果,当该对象的计算器变为0时系统会删除该对对象。
使计数器变化的操作有:1.对象被创建p = Person(),增加1;2.对象被引用p1 = p,增加1;3.对象被
当作参数传入函数func(p),增加2,原因是函数中有两个属性在引用该对象;4.对象存储到容器对 象中l = [p],增加1 所谓的容器有,字典,list等 除了增操作还有减操作:1.对象的别名被销毁del p
减少1;2.对象的别名被赋予其他对象,减少1;3.对象离开自己的作用域,如getrefcount(对象)方法,每次
用完后,其对对象的那个引用就会被销毁,减少1;4.对象从容器对象中删除,或者容器对象被销毁,减少1 (容器
就是可以存储多个对象的对象)del删除的只是变量名,而不是对象(数据),它的作用只是减少一次对象引用当应
用次数减小到0时此时会自动回收数据也就是内存,当数据销毁后调用__del__()函数 ,如果数据不销毁则不调
用该函数。所以del__del__()不同 del负责减1操作 真正负责清理的是__del__ 包含这个方法的对象
需要谨慎使用,因为这个变量不会呗循环计数器回收。类似于c++中的析构器。

class test():
    def __init__(self):
        print("__init__")
    def __del__(self):
        print('__del__')
a=test()
del(a)
#结果:__init__ __del__ __del__
##查看引用计数
import sys 
a=[1,2,3]
a1=a
b=[a,1,2]
del a1  #del 删除的是变量而不是数据    和__del__() 不同
sys.getrefcount(a)  #查看引用计数器对某个对象的计数因为是函数所以默认加了1

有些情况下会出现循环引用,此时引用计数器不能变为0 。循环引用发生在容器身上。 要解除循环引用,需要GC机制,
1先找到所有容器。2对每一个容器对象,使用变量gc_refs来记录当前对应的应用个数。3于每个容器对象,找到其
正在引用的其他容器对象,并将这个被引用的容器对象引用计数减去1。经过步骤3后,检查所有容器对象的引用计数,
若为0,则证明该容器对象是由于循环引用存活下来的,并对其进行销毁。本质依然是通过引用计数的机制来回收内存
,如果对象很多也就是内存块很多的话,垃圾回收的过程会很复杂。
当某些内存块M经过了3次垃圾收集的清洗之后还存活时,我们就将内存块M划到一个集合A中去,而新分配的内存都划
合B中去。当垃圾收集开始工作时,大多数情况都只对集合B进行垃圾回收,而对集合A进行垃圾回收要隔相当长一段时
才进行,这就使得垃圾收集机制需要处理的内存少了,效率自然就提高了。在这个过程中,集合B中的某些内存块由于存
间长而会被转移到集合A中,当然,集合A中实际上也存在一些垃圾,这些垃圾的回收会因为这种分代的机制而被延迟
循环引用的情况

import sys
class A():
    def __init__(self):
        print('A')
    def a(self,x):
        self.a=x
class B():
    def __init__(self):
        print('B')
    def b(self,x):
        self.b=x
        
test1=A()
test2=B()
test1.a=test2  
test2.b=test1  
#此时对于A()对象的计数个数为三。当删除test1之后不能使A()的引用计数变为0 所以不会删除内存。
sys.getrefcount(test2)   

for中变量对for内函数的作用域

当我们使用for循环创建函数或者对象时,如果使用for内的变量需要考虑作用空间问题

#使用x其实程序是这样执行的首先for然后x最终会变成9,在执行li[0]() 此时x传入函数时是9
li = []
for x in range(10):
    #print(x)     #在函数没有执行前(li[0]()),for 循环中x已经执行完,x会一直为 9
    def fun():
        print(x) #一直为 9  ,fun函数在for循环中是没有被调用的
        return x
    li.append(fun)
print(li)  #9
li[0]()
#结果:9

函数内变量对函数内函数的作用域

def aa(n):
    def bb(x):
        return x+n
    return bb
aa(1)(2)

我们思考函数的返回值问题 函数以什么方式将返回值返回给变量
函数的返回值以元组的方式返回给变量,而且是一个元组

 def test():
    return 1,(2,3)
a,(b,c)=test()
a=test()#虽然有两个返回值但是我们可以使用一个参数接收

特殊符号用法

->: 一般加在一个函数的后面注释函数的输出类型,

 def test(x): -> int
    return x+2

*:当有变量输入到函数中或者类实例化时,输入的变量要求是单个字符或者数字,但输入是列表或者元组或者字典时可以用 *a 解析

a=[1,2,3]
def test(x,y,z):
    return x+y+z
b=test(*a)

** :同上只不过是字典形式

def b(*args,**kwargs):#可变列表和可变词典,且*必须位于**之前   字典必须用=号否则会被认为是args中的元素
    print(args)  #在变量前加*号表示拆分可迭代对象,
    print("----------------")
    print(kwargs)
    
b([1,2,3],3,6,
nb_epoch=2,
batch_size=3,
freeze_layers_number=4)
#结果:([1, 2, 3], 3, 6)
----------------
{'nb_epoch': 2, 'batch_size': 3, 'freeze_layers_number': 4}

条件语句

| 和& 与and和or 的区别当是数值型的时候 前者是按位取后者只看是不是0

print(1|2)
print(1&2)
#结果 按位计算结果,不支持字符串的操作
print(3 and 4) 结果为其中一个数

导包import 和from import

import 和from :import 是文件级别的导入,from只需要到大概的路劲下直接import函数或者类

import package1  #✅
import module  #✅
from module import function  #✅
from package1 import module  #✅
from package1.package2 import  #✅
import module.function1  #❌ 

_init__.py文件的作用时当使用import时首先执行__init__.py文件中的东西

字典

字典有很多神奇的特性,具有超强的功能
首先对字典进行索引,在循环的过程中

labeled_data={}
labels={"a":1,"3":3,"4":4}
for (name, i ) in labels.items():  #返回key和value
       labeled_data[name]=-1
       print(i)
print(labeled_data)

字典的常用方法 获得字典的key 和value

a={'w':1,'e':2}
a.keys() #得到一个特殊类型的数据,该数据可迭代不可索引,可进行类型转化
a.values()#同上
a.iteams() 

#得到字典values的方法
a.get('w') 的带value如果key不存在则返回默认值
#删除其中字典
a.pop('w')#如果key不存在会报错

直接迭代字典会得到字典的key值

列表

列表排序是一个非常重要的工作主要用到的函数有sort 和sorted sort默认从小到大,同时会改变原列表
sorted默认是从小到大,会生成新的list,它可以对所有可迭代对象排序,

字符串

a='\nabcdd'
b=a.rstrip().lstrip()  #移除字符串前后标志符号 注意不是inplace操作会生成新的字符串返回
b=a.strip(' d') #移除前后固定符号同时移除
b=a.split('b') #对字符串进行切片 #不是inplace操作