【python】奇淫技巧后的原理探析

文章目录

    • 前言
    • 每日三省Python
    • python 的类和实例
      • 经典类和新式类的区别
      • @ property
      • partial 函数
      • super 函数
      • 有趣的反射机制
      • Python的参数传递
      • 推导式、生成器、迭代器、装饰器
    • Python内存管理
      • 内存管理
    • Python 模块
      • os
      • sys
      • hashlib
      • time、datetime、timeit
      • 系统级模块
    • Python多线程
    • Django

前言

进入研究生后,科研压力、工程任务接踵而来,曾经自己喜爱的编程也搁置许久,虽然自己声称热衷于编程,但每每打开编辑器的时候都有些许心虚,为什么,无非是基础不牢,遇事百度,编程内容十分简单,无法自己去设计复杂的程序。这些都困扰了许久,是呀,我还有2年时间毕业,作为一名非科班的学生,在这条道路上还能走多远,可能我到现在还未明白学习编程是为了兴趣还是为了追求那份高薪,但我可以肯定的是,编程背后的原理、数学是我感兴趣的,数学的海洋使我沉迷于其中,并且能提高我对于数字的敏感性,希望这一份热爱能够支持我在这条路上一直前行,能够在毕业时达到自己满意的结果。学习很多,其中的坑也是密密麻麻,自己也没有太过理解,其中特别感谢刘江老师的python教程,本人在其中也是受益匪浅:https://www.liujiangblog.com/blog/

每日三省Python

  1. Python作为一个动态语言,其对变量处理的过于宽松和Java、c++这类静态语言形成鲜明比对。对数据类型的宽松在初学时能够少犯错误,但一旦在程序中发生数据类型的错误时,排查起来将会非常麻烦;Python提供很多种函数引用方式,非常灵活,我印象最深的便是反射机制,也算开拓了我编写程序的思路。

  2. 面向过程编程和面向对象编程:
    面向过程编程注重(变量—函数功能—函数调用)的设计,一个函数的功能是在调用前已经写死的,因此核心在于函数上面;面向对象编程注重(类—类的属性和功能设计—类的调用—函数调用),在初期,对类的设定完成了函数的整体设计,至于其中的判断、控制,需要在类的实例化中在完成,这样的好处第一是减少代码量,提高重复利用率,第二是提高数据的独立性,便于内部进行处理,有助于后期维护。

    def speak(animal):
    
        if animal == "狗":
            print("旺旺!")
        elif animal == "猫":
            print("喵!喵!")
        elif animal == "牛":
            print("哞!哞!")
        else:
            print("说人话!")
    
    a, b, c, d = "猫", "狗", "牛", "二哈"
    
    speak(a)
    speak(b)
    speak(c)
    speak(d)
    
    # 先定义一个动物的类
    class Animal:
        # 动物实例的初始化方法,需要提供动物类别和该类动物的叫声
        def __init__(self, kind, voice):
            self.kind = kind
            self.voice = voice
        # 让动物发出叫声的方法
        def speak(self):
            print(self.voice)
    
    # 实例化四种动物对象
    a = Animal("狗", "旺旺!")
    b = Animal("猫", "喵!喵!")
    c = Animal("牛", "哞!哞!")
    d = Animal("二哈", "说人话!")
    
    # 调用动物类的发声方法
    a.speak()
    b.speak()
    c.speak()
    d.speak()
    
  3. 编程和计算机基础的联系
    编程和计算机的硬件是紧密联系着的,在内存管理中我也第一次见识到python在参数传递时mutable variables和immutable variables在内存管理上的差异,导致了函数输出的结果截然不同

python 的类和实例

经典类和新式类的区别

在Python2的版本中,由任意内置类型派生的类称为新式类,同时具有新式类的特性;而不是由任意内置类型派生的类称为经典类
理解这句话的核心,在于什么是内置派生类型,正常学习过C++的同学都了解派生和继承的概念,尤其是定义父类和子类。

资料(1)
1、所谓继承,就是把父类的所有成员复制到子类中而已,只不过private成员在子类中不能被调用和访问而已,但是还是会占用子类的对象的内存
2、要想毫无保留的使用基类成员,就将父类成员定义为public或者protected
3、也就是说,子类内部访问父类成员的权限,是成员本身的访问权限和上一级父类的继承方式中权限最小的那个权限

那么内置派生类型说的就是python中已有的数据类型,我们可以通过type(varable)语句查看我们变量的类型,比如list、tuple、float等等,因此当我们的类不是继承自内置派生类型,则他们就是经典类,反之就是新式类。

# python2环境中测试
class A():pass
a = A()
if __name__ == '__main__':
    print(type(a))
    print(type([1, 2, 3]))
    
# 结果
# 
# 

由此看出在python2中,经典类的实例化,都是instance类型(有何影响:比如你创建个经典类classA和经典类classB,创建了a1、a2、b1、b2好几个实例,然后你要判断,选出相同的实例进行分类,但此时所有类型都为instance类,因此无效),而在Python3中,所有类都是继承自Object基类,也就是python3中只有新式类。类等价于类型(我创建个classA类,也就是我有了classA类型,python2中是我创建个classA类,也就是我有了instance类型)

我们真正想知道的类的类型是A还是B(也就是它来自哪个父类),而不是instance。
【python】奇淫技巧后的原理探析_第1张图片
再来进行一个有趣的尝试:

print(type(type))

在python 2.x 和 3.x 运行结果都是一样的,type类型本身也是类(新式类和内置类型的类与类型已经合并),它自己也是type类型。
类实例的类型和类的类型

print(type(A))
list.__class__
[1, 2, 3].__class__

所有的类型都是type类。从另一个角度理解,类就是type类的实例,所有的新式类,都是由type类实例化创建而来,并且显式或隐式继承自object。

类的继承顺序
经典类:深度优先
新式类:C3算法

class NewStyleClassA(object):
    var = 'New Style Class A'
class NewStyleClassB(NewStyleClassA):
    pass
class NewStyleClassC(NewStyleClassA):
    var = 'New Style Class C'
class SubNewStyleClass(NewStyleClassB, NewStyleClassC):
    pass
if __name__ == '__main__':
    print(SubNewStyleClass.mro())
    print(SubNewStyleClass.var)

python的类的探析
当我们任意调试一个python程序,可以看到程序运行时有special variablesclass variablesappsys这四个主要的属性
其中比较重要的属性有__dict__、__call__(callable()函数)、__str__、__getitem__、__setitem__、__delitem__、__iter__、__slots__
yield生成器
__module__:比如numpy中的multiply和math中的mutiply函数,他们的module属性就不一样,有助于我们了解函数的来源模块。

特殊成员和魔法方法
python不错的参考网站

【python】奇淫技巧后的原理探析_第2张图片

资料:
(1) C++的继承和派生(一)父类和派生类(子类)的介绍以及派生类的访问控制
(2) Python新式类与经典类的区别
(3) Python的类与类型

@ property

翻开海康相机的SDK源码可以看到在其代码中经常引用

class MvCamera():

    def __init__(self):
        self._handle = c_void_p()  # 记录当前连接设备的句柄
        self.handle = pointer(self._handle)  # 创建句柄指针

	#####设备的基本指令和操作#####
	# ch:获取SDK版本号 | en:Get SDK Version
    @staticmethod
    def MV_CC_GetSDKVersion():
        MvCamCtrldll.MV_CC_GetSDKVersion.restype = c_uint
        # C原型:unsigned int __stdcall MV_CC_GetSDKVersion();
        return MvCamCtrldll.MV_CC_GetSDKVersion()

@ property,也称修饰器,用于将对应的函数、方法伪装成属性,以此改写调用改方法的代码,提高操作性

【python】奇淫技巧后的原理探析_第3张图片
与此相似的还有@ staticmethod 和 @ classmethod

property
Python: 浅淡Python中的属性(property)
(静态方法与类方法

partial 函数

functools.partial:额外生成函数并赋入初值

super 函数

super在继承中让子类继承了父类的所有属性和方法,父类属性自然会用父类方法来进行初始化。

# 例1
class MyMainForm(QMainWindow, Ui_Form):
    def __init__(self, parent=None):
        super(MyMainForm, self).__init__(parent)
        self.setupUi(self)
# 例2
class Person:
    def __init__(self,name,gender):
        self.name = name
        self.gender = gender
    def printinfo(self):
        print(self.name,self.gender)

class Stu(Person):
    def __init__(self,name,gender,school):
        super(Stu, self).__init__(name,gender) # 使用父类的初始化方法来初始化子类
        self.school = school
    def printinfo(self): # 对父类的printinfo方法进行重写
        print(self.name,self.gender,self.school)

if __name__ == '__main__':
    stu = Stu('djk','man','nwnu')
    stu.printinfo()

super(Stu, self).__init__(name,gender) Stu中init函数已经变成其父类的init函数,因此引入相关参数。

有趣的反射机制

getattr setattr delattr hasattr

# visit.py

import commons

def run():
    inp = input("请输入您想访问页面的url:  ").strip()
    if inp == "login":
        commons.login()
    elif inp == "logout":
        commons.logout()
    elif inp == "home":
        commons.home()
    else:
        print("404")


if __name__ == '__main__':
    run()

如果网站很多,那我们就要在if块中添加非常多的判断,这时我们可以使用反射机制,将输入转换成对应模块的执行语句(这点和eval()exec()有点类似)

# visit.py
import commons

def run():
    inp = input("请输入您想访问页面的url:  ").strip()
    if hasattr(commons,inp):
        func = getattr(commons,inp)
        func()
    else:
        print("404")

if __name__ == '__main__':
    run()

这也体现出Python作为动态语言的灵活性

反射机制

Python的参数传递

*args **kwarg的作用
***的作用

参数类型

推导式、生成器、迭代器、装饰器

推导式

[x * x for x in range(1, 10)]
[x * x for x in range(1, 11) if x % 2 == 0]
[a + b for a in123' for b in ‘abc']
dic = {"k1":"v1","k2":"v2"}
[k+":"+v for k,v in dic.items()]

迭代器:自己写的类成为一个迭代器,需要在类里实现__iter__()和__next__()方法,除此以外map函数也可用于生成迭代器,filter函数可用于生成过滤器
生成器:不同于推导式,生成器制作一个生成函数,在使用的时候才计算对应值;推导式是完成所有值的计算并生成一个数据类型,这需要占用大量内存空间

g = (x * x for x in range(1, 4))

for i in g:
    print(i)
# or
next(g)
# 斐波那契函数
def fibonacci(n):    
    a, b, counter = 0, 1, 0
    while True:
        if counter > n:
            return
        yield a             # yield让该函数变成一个生成器
        a, b = b, a + b
        counter += 1

fib = fibonacci(10)           # fib是一个生成器
print(type(fib))
for i in fib:
    print(i, end=" ")

装饰器:写代码要遵循开放封闭原则,简单来说,已经实现的功能代码内部不允许被修改,但外部可以被扩展。如果将开放封闭原则应用在上面的需求中,那么就是不允许在函数f1 、f2、f3…f100的内部进行代码修改,但是可以在外部对它们进行扩展。

def login():
    print("认证成功!")

def f1():
    login()
    print("业务部门1数据接口......")
def f2():
    login()
    print("业务部门2数据接口......")
def f3():
    login()
    print("业务部门3数据接口......")
def f100():
    login()
    print("业务部门100数据接口......")

#各部门分别调用
f1()
f2()
f3()
f100()
def outer(func):
    def inner():
        print("认证成功!")
        result = func()
        print("日志添加成功")
        return result
    return inner

@outer
def f1():
    print("业务部门1数据接口......")

@outer
def f2():
    print("业务部门2数据接口......")
@outer
def f3():
    print("业务部门3数据接口......")

@outer
def f100():
    print("业务部门100数据接口......")

#各部门分别调用
f1()
f2()
f3()
f100()

推导式
迭代器
生成器
装饰器
高阶装饰器wrapper
python 装饰器详解
闭包与修饰器

Python内存管理

内存管理

有一段简单的代码

def func(a=[]):
   a.append("A")
   return a

print(func())
print(func())
print(func())

我一开始以为答案是三个[A],但没想到其答案是

['A']
['A', 'A']
['A', 'A', 'A']

因为Python函数体在被读入内存的时候,默认参数a指向的空列表对象就会被创建,并放在内存里了。因为默认参数a本身也是一个变量,保存了指向对象[]的地址。每次调用该函数,往a指向的列表里添加一个A。a没有变,始终保存的是指向列表的地址,变的是列表内的数据!

def func(a=None):
# or
def func(a=-1)
    # 注意下面的if语句
    if a is None:
        a = []
    a.append("A")
    return a

print(func())
print(func())
print(func())

关于这点的解释涉及函数中可变对象和不可变对象在参数传递时的内存操作,可变对象在内存中的引用是固定的,而不可变对象每次都会新建一个内存,因此造成两种差异。这种机制还在浅拷贝和深拷贝中有所体现。
python的byte和C的byte不一致:python的int类型为28个字节而C语言仅有4个字节,因为int类型是一种Python对象,和list、tuple、string都是python中后定义的一种结构体,因此容量和效率都比C低很多。
sys.getsizeof()、xx.__sizeof __和ctypes.string_at后的元素长度是不一致的
我们使用ctypes中的函数进行分析

import ctypes

def print_bytes(bs):
    for i, b in enumerate(bs):
        if i % 8 == 0 and i != 0:
            print(' ', end='')
        if i % 16 == 0 and i != 0:
            print()
        print('{:02X} '.format(b), end='')
    print('\n')

def dump_mem(address, size):
    p = ctypes.cast(address, ctypes.POINTER(ctypes.c_char * size))
    return p.contents.raw

def print_mem(address, size):
    mem = dump_mem(address, size)
    print_bytes(mem)

def print_obj(obj, size):
    print_mem(id(obj), size)
>>> print_obj(1, 32)
35 03 00 00 00 00 00 00  10 3D A0 11 FF 7F 00 00  
01 00 00 00 00 00 00 00  01 00 00 00 00 00 00 00 
>>> print_obj(0xABCDEF, 32)
03 00 00 00 00 00 00 00  10 3D A0 11 FF 7F 00 00  
01 00 00 00 00 00 00 00  EF CD AB 00 68 02 00 00 

对于Python的两个int对象,可以看到,第一组8字节,是索引数量,可见索引数字1的有非常多,索引0xABCDEF的就没几个了。第二组8字节,是一模一样的,这里就是int的type object的地址,也就是PyObject里的*ob_type

>>> hex(id(int))
'0x7fff11a03d10'

int对象的type object,显然就是"int"了,可见地址果然完全对的上。

print(hex(id(bytes)))
0x7fff57eeb0f0
print(hex(id(str)))
0x7fff57ef7e50
print(hex(id(int)))
0x7fff57ef51e0
print(hex(id(float)))
0x7fff57ef3c00
b'\x02\x00\x00\x00\x00\x00\x00\x00\xe0Q\xefW\xff\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x06\xb2\x01\x00'
0200000000000000e051ef57ff7f0000010000000000000006b20100
b'\x02\x00\x00\x00\x00\x00\x00\x00\xe0Q\xefW\xff\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x07\xb2\x01\x00'
0200000000000000e051ef57ff7f0000010000000000000007b20100

abcd
0200000000000000507eef57ff7f00000400000000000000d383d22799cd22c3e530949e4602000000000000000000006162636400
abcde
0300000000000000507eef57ff7f000005000000000000001c0ce80c606a9690e530949e460200000000000000000000616263646500
b'ab'
0200000000000000f0b0ee57ff7f00000200000000000000987577c4fd92a1fc616200

10
b'\x15\x01\x00\x00\x00\x00\x00\x00\xe0Q\xefW\xff\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00'
1501000000000000e051ef57ff7f000001000000000000000a000000
11
b'\x0e\x01\x00\x00\x00\x00\x00\x00\xe0Q\xefW\xff\x7f\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'
0e01000000000000e051ef57ff7f000001000000000000000b000000

像C里面,通过指针改一个int的值大多是很正常的操作,在Python里面就完全不ok了,因为Python里的int是immutable的。a += 1在Python里创建了一个拥有新的值的新对象,原来的a已经不见了。 如果用指针强行把值改变,大概会出现完全不对的情况。

Python 函数和内存分析
python的内存机制
getsizeof函数
Python内存管理看我这一篇就完事了!!!!
pep-0237
Python integer objects implementation
使用Python的ctypes查看内存
没白熬夜,终于把Python的内存管理机制搞明白了
python内存管理(通俗易懂,详细可靠)

Python 模块

os

os.walk()

import os

try:
    for root, dirs, files in os.walk(r"c:\python36"):
        print("\033[1;31m-"*8, "directory", "<%s>\033[0m" % root, "-"*10)
        for directory in dirs:
            print("\033[1;34m    %s\033[0m" % directory)
        for file in files:
            print("\t\t%s" % file)
except OSError as ex:
    print(ex)

sys

sys.stdoutsys.stdin

import sys
import time


def bar(num, total):
    rate = num / total
    rate_num = int(rate * 100)
    r = '\r[%s%s]%d%%' % ("="*num, " "*(100-num), rate_num, )
    sys.stdout.write(r)
    sys.stdout.flush()


if __name__ == '__main__':
    for i in range(0, 101):
        time.sleep(0.1)
        bar(i, 100)

hashlib

常见hash算法:MD5、SHA256,可用于密码加密领域,加盐操作

time、datetime、timeit

【python】奇淫技巧后的原理探析_第4张图片

系统级模块

文件操作类:fileinput、shutil、zipfile、tarfile、getpass、json、logging
消息队列类:queue、pickle、shelve
进程类:subprocess

Python多线程

python多线程

Django

Django教程

你可能感兴趣的:(Python,python)