Python 学习笔记

127 阅读9分钟

basic concept

  • python 中一切皆为对象,即使类本身也是一种对象(类对象/Class Objects),所以避免使用其他语言中的instance,而是instance object,class object,method object.
  • 使用attribute(属性)来表达成员的含义,有数据属性以及方法属性,一个object=数据属性+方法属性
  • functionmethod有区别,前者一般指普通函数,后者一般指的是bounding method(instance object 的方法属性)
  • 我的形象理解,python的世界中,有三种层级的对象,分别负责创建下一级别的对象,由上到下是type关系,不是继承关系
    • 第一阶级:metaclass/type object。
    • 第二阶级:class object。
    • 第三阶级:instance object.
    • 常说的UML图中,描述的是同一阶级内部之间的继承关系(主要第二层),与不同阶级无关。python可以用issubclass() 检查。
    • type()可以查看其上一层级的object,或者说这个对象是由哪个类对象创造的
    • metaclass比较神奇,因为其type为自身。其父类也是自身。
    • object类对象,跨越一二层,是他们公共的父类。
    • 第一阶层,实际编程很少需要考虑。
  • python中所有的方法都是虚方法
  • 与C不同,在函数内部默认不使用全部变量,除非使用global指定,且if,for,while,try之类的关键字不会产生作用域。
  • python3 中的round()函数并不是四舍五入的,应该是 四舍,六入,五选偶,这样做是保证总误差不会被累积。
  • lambda函数的写法 lambda x: x+1
  • map() 在python中一个函数,而非Dict(),注意与C++区别,他会给每个元素执行一遍函数,并且返回一个可以迭代的map对象。也可以通过list()等方法,转成list对象。
  • sort方法会inplace修改对象,sorted不会。

string

a = "123"
b = a
a = "456" # 字符串之间的拷贝都是深拷贝
print (b)
path_t =r"D:\worksapce_python\20160426_cp\training" # 禁止使用转义字符
a = 123
b = 456
c1 = f"a={a}, b={b}" # 两种初始化方法 1
c2 = "a={}, b={}".format(a,b) # 两种初始化方法 2

container

#list, 顺序容器,支持随机访问,类似c++中的std::vector.
data = list()
data = [] #两种初始化方法
#set/dict, 无序哈希表,不是RB tree,但在python3中遍历顺序与插入顺序相同。
data = {} #{}默认带key,即dict
data = dict()
data = set()
#tuple, 不可改变的list
data = ()
data = tuple()

class

class MyBaseClass:  # 类命名一般使用驼峰
    def __init__(self):  # 初始化函数,self是约定俗成的,不是强制的
        self.name = "name"  # 在这里增加数据属性,也可以在外部添加
        self._counter = 0  # 私有变量一般使用_开头,外部也可以强制访问

    def print_name(self):  # 方法属性命名一般使用下划线开头
        print(self.name, "this is the ", self._counter, "time")
        self._counter += 1

class MyDerivedClass(MyBaseClass):
    pass

if __name__ == "__main__":
    base = MyBaseClass()
    base2 = base
    derived = MyDerivedClass()
    print(issubclass(MyDerivedClass,MyBaseClass)) # issubclass 只是用来衡量两个类对象之间的关系。
    print(isinstance(base,MyBaseClass)) # isinstance 用来衡量类对象与实例对象之间的关系。
    print(type(base))
    print(base.__class__) # 显示object的type/类
    print(type(type(base))) # type 是元类
    base.print_name()
    '''
    print_name是一个function object,是MyBaseClass的一个属性,base.print_name会调用`_get_`生成一个临时的boudingd method方法。然后再调用
    '''

with

with b as a:
    code

等价流程

  1. a = b.enter
  2. code
  3. b.exit

as a 是一个option,不是必须的

异常处理

  1. try 模块中通过raise抛出异常
  2. {except else(可选)}处理异常情况 or 不处理
  3. finally(可选) 执行相关代码
  4. 如果异常没有处理,则继续向上传送异常。

读取可变长度参数

def print_two(*args):
    arg1, arg2 = args
    print(f"arg1: {arg1}, arg2: {arg2}")

numpy

numpy 中matrix是array的一个子类,是严格意义上的二维矩阵,区别是*在matrix是矩阵乘法,而array是代表对应位置的元素相乘。

叉乘(标准)点乘(逐元素)
arraydot @multiply *
matrixdot @ *multiply
  • @新版本推荐使用这个符号来表示矩阵的乘法

numpy 会自动把列向量转成行向量, 转置操作对行向量无效,需要reshape。

  • np.array([1,2,3]) //这是一个数组(行向量),shape为(3,),无法执行转置T,可以reshape
  • np.array([[1,2,3]]) //这是一个矩阵,shape为(1*3)

产生随机数 np.random.rand(50)*100
产生顺序值 np.arange(3,7,2)
将二维变成1维 x.flatten()

里面的索引方式都是先行后列的,创建shape的时候也是如此。

dataframe

  1. dataframe 是一列列series对象(numpy的增强版)的集合.
  2. 列读取
    • df['time'] //取出其中的一列,取出的是series对象,这里不能用数字/index,需要string
    • df[['time']] //取出其中的一列,取出的是一个data frame 对象, 因为['time'],实际上是一个list.
  3. 行读取
    • df.iloc[0] /基于row number,输入行号,不是索引值,这里返回同样是一个series对象
    • df.iloc[[0]] //取出的就是一个dataframe对象
    • df.loc["john"] //基于index的具体内容

注意,[]不一定完全是按照列读取,如果使用df[1:3],是按照行去读取,这里容易出错。
Ref: pandas.pydata.org/pandas-docs…

numpy、series、dataframe的区别

  • series是带索引的增强版numpy,其索引可以是任意类型的数据,支持分区提取,例如data['c':'f']

    在索引的时候,中括号内的整数其实指的是索引数组中的实际值,而不是位置值,如果,我们按照位置来取值的话,反而会出错。例如a[0]

  • dataframe相同索引的Series对象排列,就形成了DataFrame。

constants, variables, operations in tensorflow

# first, create a TensorFlow constant
const = tf.constant(2.0, name="const")

# create TensorFlow variables
b = tf.Variable(2.0, name='b')
c = tf.Variable(1.0, name='c')

# now create some operations
d = tf.add(b, c, name='d')
e = tf.add(c, const, name='e')
a = tf.multiply(d, e, name='a')

# setup the variable initialisation
init_op = tf.global_variables_initializer()

with tf.Session() as sess:
    # initialise the variables
    sess.run(init_op)
    # compute the output of the graph
    a_out = sess.run(a)
    print("Variable a is {}".format(a_out))
    print("Variable a is {}".format(const))
  1. as the Python code runs through these commands, the variables haven’t actually been declared as they would have been if you just had a standard Python declaration (i.e. b = 2.0). Instead, all the constants, variables, operations and the computational graph are only created when the initialisation commands are run.
  2. d e f is not a variable but an operation

参数传递

所有的都是引用传递,但是不可变对象重新赋值的时候,会产生一个新对象。

  • 可变对象(比如字典或者列表)
  • 不可变对象(比如数字、字符或者元组)

对象的几种移除方法差别

  • remove 移除对应value的元素
  • pop 移除对应index的元素,并且返回改对象
  • del 移除对应index的元素

Ref: stackoverflow.com/questions/1…

assert用法

#if condition returns False, AssertionError is raised:
assert x == "goodbye", "x should be 'hello'"

判断相等

  • == 判断两个对象是否相等
  • is 判断两个对象是否为同一个对象

Iterator

常用以下函数

  • iter() 可以返回第一个元素的迭代器
  • next() 跳到下一个元素,但是返回当前元素
  • filter() 函数返回指向所有合格的元素的迭代器

Generator

Generator 是Iterator的子类,print(issubclass(Generator,Iterator))。通过yield关键字创建,主要有一下两种方式

# way 1
num_generator = (x*2 for x in range(1,3))
# 不能使用中括号`num_list = [x*2 for x in range(1,3)]`,否则就变成了list

# way2
def firstn(n):
    num = 0
    while num < n:
        yield num # 下一次进入从这里执行。
        num += 1

Decorator

装饰器就是给函数增加前置或者后置的操作,内部是执行装饰器函数,然后把被装饰的对象传入。

import time

def decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        func()
        end_time = time.time()
        print(end_time - start_time)

    return wrapper

@decorator
def func():
    time.sleep(0.8)

func() # 函数调用# 输出:0.800644397735595
# 执行func(),其实是在执行 decorator(func)

类装饰器与普通装饰器也类似,__call__实际上类似重载了()运算符

class Decorator(object):
    def __init__(self, f):
        self.f = f
    def __call__(self):
        print("decorator start")
        self.f()
        print("decorator end")

@decorator
def func():
    pass

p = Decorator(func) # p是类Decorator的一个实例
p() # 实现了__call__()方法后,p可以被调用

Descriptor

Definition:一种特殊的class,如果一个Class有__get__,__set__,__delete__方法,那么这个Class就是一个Descriptor.

  • 当外界access(get,set,delete)这个变量时,就会自动调用相应的函数
  • Descriptors only work when used as class variables. When put in instances, they have no effect.
  • 所有的function都包含__get__, 都是descriptor, 准确点是non-data descriptor.
  • 等同代码 f.x would cause type(f).__dict__['x'].__get__ to be called, and f.x = 3 would call type(f).__dict__['x'].__set__(3).
class Dog:
    def __get__(self, obj, objtype):
        return "call the get method"

class Cat:
    pass

class House:
    d = Dog()
    c = Cat()
print(type(House.d)) # House.d返回是一个str 对象
print(type(House.c)) # House.c返回是一个cat对象
print(type(House().d))
## 使用举例,用作数据转换
class Number:
    def __init__(self):self.value=0
    def getx(self):
        return -self.value;
    neg_value=property(getx,None,"what")


n = Number();
n.value =10
print(n.neg_value)

Ref: codesachin.wordpress.com/2016/06/09/…

__dict__

__dict__ 可以打印出对象所有的成员,及其对应的值。

print(House.__dict__) # 两者等价
print(vars(House))

访问属性的查找顺序(有待纠正)

  1. descriptor
  2. instance.dict
  3. class.dict

class attribute 是不出现在 instance attribute 里面的。

实例方法与类方法同名

实例方法会被覆盖

class Bar(object):
    def fun(self):
        print("instance method")
    @classmethod
    def fun(cls):
        print("class method")

a = Bar()
a.fun()
Bar.fun()

两者都会print class method

访问attribute/method原理

Object访问attribute以及调用instance method都是线调用__getattr__函数,return一个对象。这个对象如果callable, 就成了函数

例外情况:如果这个attribute已经初始化,就不会调用getattr函数。 __getattribute__ 不管变量是否已经存在,都会调用。

__get____getattr____getattribute__ 区别

  1. 查找lookuptable
  2. 找到,执行(默认/override)__get__函数
  3. 找不到,执行(默认/override)__getattr__函数

__getattribute__ 将会屏蔽上述所有操作。

设置attribute

对某个属性进行赋值时,调用__setattr__函数

del 某个属性,其实也是调用函数

        @name.deleter
        def name(self):
            self._name = 'Number Six'

__call__

python 的__call__属性就是重载call函数

class Test:
    def __call__(self,a,b):
        print(a+b)
t = Test()
t(1,2)

object() is shorthand for object.call()

包管理

导入当前目录的文件

test.py

def add(x,y):
    return x + y

main.py

import test
test.add(1,2)

导入package

在一个文件夹下如果有__init__.py的文件, import这个package的时候会自动执行。 创建一个pac的文件夹,包含__init__.py与test.py文件 init.py

import pac.test
add = pac.test.add

test.py

def add(x,y):
    return x + y

main.py

import pac as p
p.add(1,2)
  • __init__.py 这个文件不是必须的,如果有的话,在import的时候,会自动执行某些初始化函数。
  • import 导入的是module,不能直接导入package,如果有import package,本质上是运行的__init__.py的函数。stackoverflow.com/questions/3…
  • from package import module可以进行部分的导入,与import package.module同样可以导入外部模块,区别是前者使用的时候可以module.fun(),后者需要一直加package.module.fun(),