sonny's python是我2021年学习python时候的笔记。学习的内容主要来自runoob.com 以及 Python教程 - 廖雪峰的官方网站 。笔记主要记录了在学习过程中我觉得重要且值得探索的特性,所以该笔记无法作为一份入门教程而存在,希望以后可以通过它唤醒我们的脑海中关于python的记忆。

第一部分 基础语法

1.1 变量

python属于动态语言,变量是无类型的。

name = 'sonny'
print(sonny)

简单来说,就是不会像Java一样还要指定类型String name = “sonny”

1.2 常量

通常大写表示。

1.3 注释

| 符号 | 说明 |
| —- | ——– |
| # | 单行注释 |
| ''' | 多行注释 |
| ““” | 多行注释 |

class KeyError(LookupError):
    """ Mapping key not found. """
    def __init__(self, *args, **kwargs): # real signature unknown
        pass

    def __str__(self, *args, **kwargs): # real signature unknown
        """ Return str(self). """
        pass

1.4 运算符

算数运算符

只列举几个常见的

| ** | 表示 N次方 |
| —- | ———— |
| // | 表示向下取整 |

py的除法
  1. / 表示除,得到的是浮点数,如
    print(10/3)
    
    结果为
    3.333333333335
    
`//` 又称之为地板除,结果取整数

10 // 3

结果为

3

#### 逻辑运算符

| 符号 | 说明 |
| ---- | -------- |
| and   | 与 |
| or | 或 |
| not  | 非 |

#### 成员运算符

在字符串,列表,元组中可用。
- `in`  在
- `not in` 不在

numbers = [1, 2, 3, 4]
a = 1
b = 10

print(a in numbers)
print(b not in numbers)

str = '1234ABCD'

c = '1'
print(c in str)

输出全部为`True`

#### 身份运算符

`is` 判断两个标识是否引用自一个对象
`is not` 判断两个标识是否不是引用自一个对象

> tips: ==用于判断是否等值



#### python中的经典表达式

a=1
b=2

a,b=b,a+b

print(a)
print(b)

要弄清楚`a`和`b`最终的值,就要弄清楚`a,b=b,a+b`是如何计算的。其实 很简单,如下图所示

![image-20210316091654648](https://aliyun-oss-chingsblog.oss-cn-shenzhen.aliyuncs.com/blog/typora/2021/03/16/20210316151346.png)

就是把`b`赋值给`a`,把`a+b`赋值给`b`。其实就是 `(a, b) = (b, a+b)`

类似的可以推测

a,b=b,a

这是将`a`和`b`两个值互换。



### 1.5 数据类型

字符串 `str`;
字节类型 `bytes`,一般在前面加上一个小写的`b`,如   `b'ABC'` ;

### 1.6 格式化输出

1. 第一种方式

| 符号 | 说明 |
| ---- | -------- |
| %d    | 整数 |
| %f  | 浮点数 |
| %s  | 字符串 |
| %x  | 十六进制数 |

需要注意的是, 格式化的时候需要额外加上一个%,后面紧接着替换值,例如:

print('你好呀,%s' % '马大姐')

上面将会输出

你好呀,马大姐

2. format()

print('this is {0}, hello {1}'.format('python', 'world'))

   上面将会输出`this is python, hello world`.

3. f-string
   以f开头的字符串,将变量包在{ } 里面,然后自动替换

s='world'
print(f'hello {s}')

   上面将会输出`hello world`.

### 1.7 数组

#### list

列表,用{ } 来表示,list中的元素类型可以不同。

classmate = ['Bob', 'Allen', 'Tracy']
L = [1, 'S', True]

print(len(classmate))

““”
表示获取正数第一个/正数第二个
““”
print(classmate[0])
print(classmate[1])

““”
表示获取倒数第一个/倒数第二个
““”
print(classmate[-1])
print(classmate[-2])

classmate.append('Jack')
classmate.pop()

常用方法

| 方法                                  | 描述                                       |
| ----------------------------------- | ---------------------------------------- |
| list.append(obj)                    | 在列表末尾添加新的对象                              |
| list.count(obj)                     | 统计某个元素在列表中出现的次数                          |
| list.extend(seq)                    | 在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)从列表中找出某个值第一个匹配项的索引位置 |
| list.index(obj)]                    | 从列表中找出某个值第一个匹配项的索引位置                     |
| list.insert(index, obj)             | 将对象插入列表                                  |
| list.pop([index=-1])               | 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值           |
| list.remove(obj)                    | 移除列表中某个值的第一个匹配项反向列表中元素                   |
| list.reverse()                      | 反向列表中元素                                  |
| list.sort( key=None, reverse=False) | 对原列表进行排序                                 |
| list.clear()                        | 清空列表                                     |
| list.copy()                         | 复制列表                                     |


#### tuple

元组,初始化以后便不可改变的有序列表。用()来表示。需要注意的是,当元组只有一个元素的时候,规定必须手动加上一个`,` ,否则会被当作同数学中的括号.

roommate = (1, 'S', True)
roommate_of_mine = (1,)

### 1.8 条件判断

#### if /else

age = 20
if age <= 3:

print('3岁小孩')

elif (age > 3) and (age < 18):

print('小屁孩')

else:

print('男人')
### 1.9 循环

#### for

for i in list:

print(i)
看起来很方便,但是如果你是从其他语言(C、Java等)转过来,有时候你会想要拿到list的下标。这时候,只用for肯定不行。在python中,这种场景一般可以用`enumerate`来做。

my_list = ['apple', 'banana', 'grapes', 'pear']
for counter, value in enumerate(my_list, 0):

print(counter, value)

输出:

(1, 'apple')
(2, 'banana')
(3, 'grapes')
(4, 'pear')

上面代码,意思是迭代的同时进行计数(自行指定),让my_list从指定数字开始迭代。这时候,如果你从0开始迭代,那么`counter`就是对应的下标了。



#### while

n = 20
while n > 0:

print(n)
n = n - 2
### 1.10 键值对

就类似java中的`Map/Set`

#### dict

无序集合

d = {'Bob':100, 'Jack': 98}

d['Bob'] = 99

value = d.get('Jack')

value = d.get('Jack', -1)

d.pop('Jack')

#### Set

key不重复的集合

s = set([1, 2, 3])

s.add(4)

s.remove(2)

### 1.11 枚举

#### 创建枚举

一个正常的枚举是继承自`Enum`的。

from enum import Enum

class Color(Enum):

RED = 1
BLACK = 2
BLUE = 3
#### 枚举常用操作

枚举成员不等于枚举的值,枚举成员是Color的一个实例

print(Color.BLUE)

output: Color.BLUE

枚举成员的类型

print(type(Color.RED))

output:

按值查找枚举成员

print(Color(3))

output: Color.BLUE

按名称查找枚举成员

print(Color['BLUE'])

output: Color.BLUE

输出枚举名

print(Color.BLUE.name)

output: BLUE

输出枚举值

print(Color.BLUE.value)

output: 3

遍历枚举

for color in Color:

print(color.value)
#### 枚举成员值重复

`python`中的枚举元素的值是可重复的,第二个重复的枚举成员相当于第一个的别名。

如下面的`BLUE_alias`,是`BLUE`的别名。

from enum import Enum

class Color(Enum):

RED = 1
BLACK = 2
BLUE = 3
BLUE_alias = 3
注意:

- 按值查找枚举成员时,只会转成原名成员(`BLUE`),不会转成别名成员(`BLUE_alias`);
- 按名称查找枚举成员时,就算输入别名(`BLUE_alias`),也会直接转成原名成员(`BLUE`);
- 使用`for`遍历时,不会输出别名枚举成员(`BLUE_alias`);

如下所示

按值查找枚举成员。注意:只会转成原名成员(BLUE),不会转成别名成员(BLUE_alias)

print(Color(3))

output: Color.BLUE

按名称查找枚举成员。注意:就算输入别名(BLUE_alias),也会直接转成原名成员(BLUE)

print(Color['BLUE_alias'])

output: Color.BLUE

遍历枚举

for color in Color:

print(color)

output: Color.RED Color.BLACK Color.BLUE

#### @unique注解

`@unique`不允许枚举值重复

from enum import Enum, unique

@unique
class Color(Enum):

RED = 1
BLACK = 2
BLUE = 3
BLUE_alias = 3
上面代码执行时会报错

Traceback (most recent call last):
File “C:/Users/广西北部湾银行/Desktop/python practice/Enum.py”, line 5, in

class Color(Enum):

File “E:\develop\python3.7\lib\enum.py”, line 869, in unique

(enumeration, alias_details))

ValueError: duplicate values found in : BLUE_alias -> BLUE

### 1.12 异常处理

try:

...(要执行的代码)

except ***Error:

...(异常时执行)

else:

...(正常运行时执行)

finally:

...(必定执行的代码)
除了`try`关键字, 下面的关键词(`except/else/finally`)均为可选,其中,可以存在多个`except`。

#### 抛出异常

raise。
手动抛出异常 

if x>5:

raise  Exception("x不能大于5")
不处理异常,向上抛出

try:

...(要执行的代码)

except ***Error:

raise
`pass` 关键字表示忽略异常

#### 自定义异常

继承自Exception类

class MyError(Exception):

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

def __str__(self):
    return self.value

try:

raise MyError(2)

except MyError as e:

# print(e.value)
raise
### 1.13 文件操作

#### open()

open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。

使用`with`语句,我们就可以不用显式地调用 `close()` 方法,不管是否出现异常都会自动`close()`,这和手动`try...finally`的效果是一样的。

如下所示:

with open('/path/to/file', 'rw') as f:

print(f.read())
f.write('Hello, world!')
但实际上`open()`有这么多参数

open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)

参数说明:

* file: 必需,文件路径(相对或者绝对路径)
* mode: 可选,文件打开模式
* buffering: 设置缓冲
* encoding: 一般使用utf8
* errors: 报错级别
* newline: 区分换行符
* closefd: 传入的file参数类型
* opener: 设置自定义开启器,开启器的返回值必须是一个打开的文件描述符。

其中,mode有很多可选项,这里列出常用的几个:

- 如果你想读取文件,传入`r`

- 如果你想读取并写入文件,传入`r+`

- 如果你想覆盖写入文件,传入`w`

- 如果你想在文件末尾附加内容,传入`a`



### 1.14 面向对象

#### 类定义

类名大写开头

class ClassName:

....
#### 实例化

x = ClassName()

#### 构造方法

def init(self):

......
一般来说,类中第一参数永远是`self`(叫别的名也可),表示该实例自身,类似于`java`中的`this`。并且,调用该方法时,不用传递该参数。

class Student:

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

def total_score(self, math_score, social_score):
    return print(f"studentName: {self.name}, id: {self.student_id}. "
                 f"totalScore: {math_score+social_score}")
调用

y = Student('sonny', '10086')
y.total_score(1, 2)

#### 私有方法

在`Java`中,表示私有一般有 `private/protected/public`来表示方法或者变量的可见范围。

python中约定,私有属性一般可以用`_`或者`__`来表示,如`__name`, `__add()`。但是二者略有差别。

#### 区分`_`和`__`

##### `_` 表达了一种受保护的约定

class Cat:

def _method(self):
    print('method from Cat')

littleCat = Cat()
littleCat._method();

用一个下划线的时候,按`python`的约定是表示私有化。在`pycharm`引用`littleCat`实例的时候,是不会提示有`_method()`方法的。但是如果你强行手动敲上`_method`, 在`pycharm`中的也会友情提示你“访问了被保护的成员方法”,如下图所示。


![pycharm访问被保护方法提示](https://aliyun-oss-chingsblog.oss-cn-shenzhen.aliyuncs.com/blog/typora/2021/03/10/20210310092815.png)



而上述代码实际上是可以执行成功的,结果为

method from Cat

> 结论,\_ 只是表达了一种受保护的约定,但是仍然可以调用(但是很反对这么做)


##### `__`是一种伪私有化

首先,明确一下私有方法的两个特点:

1. 在类的外部无法被调用;
2. 不能被子类所覆盖;

然后,我们来观察下面的代码,并试着执行一下

class Cat:

def __aowu(self):
    print('嗷呜~ from Cat')

littleCat = Cat()
littleCat.__aowu()

发现执行上述代码会报错,如下所示

File “…“, line 21, in

littleCat.__aowu() 

AttributeError: 'Cat' object has no attribute '__aowu'

可以发现,提示了 **方法不存在** ,而不是 **调用了私有方法** 。为什么呢?

原来,python中用到了`name mangling`技术,这个技术主要被各大语言用来解决命名冲突问题。

在py中`name mangling`的直接作用是:把 **__方法名** 变成 **_类名__方法名** 。

所以,这就解释了为什么上述代码在调用`__aowu()`的时候会报 **方法不存在** 的异常,因为它已经变成 `_Cat__aowu()` 了。

那么,若是调用`_Cat__aowu()`,应该可以执行成功吧?

class Cat:

def __aowu(self):
    print('嗷呜~ from Cat')

littleCat = Cat()
littleCat.Cat_aowu()

结果如下。是可以执行成功的,和预想的一样

method from Cat

到这里,得到的一个重要结论是:


> 使用双下划线,会把 **\_\_方法名** 变成 **\_类名\_\_方法名** 。


这时候,在Cat 之外无法调用 `\_\_aowu()` ,这就满足了私有方法的“无法在定义它的类之外被调用”特点。

在考虑一下,其实会发现,私有方法的第二个特征“不能被子类所覆盖”其实也满足了。来看一下。

class Cat:

def __aowu(self):
    print('嗷呜~ from Cat')

def aowu(self):# 用于暴露一个接口给子类去调用Cat的私有方法__aowu()
    self.__aowu() # 从Cat继承而来的方法,间接调用父类Cat的的__aowu()

class Dog(Cat):

def __init__(self):
    self.__aowu()
    self.aowu()

def __aowu(self):
    print('嗷呜~ , learn from Cat!')

Dog()

执行结果为

method from Cat
method from Dog, rewrite from Cat!

有一个`Dog`类继承了`Cat`,`Dog`里面也有一个 `\_\_aowu()` 。根据上面的结论,`Dog`的`\_\_aowu()` 其实是 `\_Dog\_\_aowu()` ,而不是Cat中的 `\_Cat\_\_aowu()` ,所以自然不会覆盖。

如果说 \_\_(双下划线) 是为了表达 **私有** 的概念,那么上面的例子中,Cat的实例`littleCat`不能调用私有方法 `\_\_aowu()` ,其实已经达到了 **私有** 的效果,只不过它是悄悄把方法名修改了,从而达到 **私有** 的效果 ,而并不是语言层面上的 **私有** ,所以说 这是一种 **伪私有化** 。

####  双下划线方法的覆盖

现在你知道了python用了`name mangling`,也知道在这是一种“伪私有”,如果你想要强行覆盖私有方法,也不是不行。来看下面的代码

class Cat:

def __aowu(self):
    print('嗷呜~ from Cat')

def aowu(self):# 用于暴露一个接口给子类去调用Cat的私有方法__aowu()
    self.__aowu()

class Dog(Cat):

def __init__(self):
    self.__aowu() # 调用Dog自身__aowu()
    self.aowu() # 从Cat继承而来的方法,间接调用父类Cat的的__aowu()
    self._Cat__aowu() # 调用Dog自己写的__aowu()

def __aowu(self):
    print('嗷呜~ , learn from Cat!')

def _Cat__aowu(self):
    print('嗷呜222~ , learn from Cat!')

Dog()

可以看到,如果你在子类Dog中,手动增加一个`_Cat__aowu() `方法。如果Dog把父类Cat的`__aowu()` 给重写了,那么第二行输出的应该是 “嗷呜222~ , learn from Cat!”。

试着执行一下,果不其然。
运行结果为

嗷呜~ , learn from Cat!
嗷呜222~ , learn from Cat!
嗷呜222~ , learn from Cat!

所以,非特殊情况不要给一个方法以 `_类名__` 开头。一旦取了这种妖魔鬼怪的名字,指不定就把什么私有方法给给覆盖了。



#### 类的专有方法
| 方法              | 释义            |
| --------------- | ------------- |
| \_\_init\_\_    | 构造函数,在生成对象时调用 |
| \_\_del\_\_     | 析构函数,释放对象时使用  |
| \_\_repr\_\_    | 打印,转换         |
| \_\_setitem\_\_ | 按照索引赋值        |
| \_\_getitem\_\_ | 按照索引获取值       |
| \_\_len\_\_     | 获得长度          |
| \_\_cmp\_\_     | 比较运算          |
| \_\_call\_\_    | 函数调用          |
| \_\_add\_\_     | 加运算           |
| \_\_sub\_\_     | 减运算           |
| \_\_mul\_\_     | 乘运算           |
| \_\_truediv\_\_ | 除运算           |
| \_\_mod\_\_     | 求余运算          |
| \_\_pow\_\_     | 乘方            |