diff --git a/wiki/programming-language/Python/入门/函数方法.md b/wiki/programming-language/Python/入门/函数方法.md index 0b8e6652..5720e946 100644 --- a/wiki/programming-language/Python/入门/函数方法.md +++ b/wiki/programming-language/Python/入门/函数方法.md @@ -1,8 +1,14 @@ --- -id: 函数方法 title: 函数方法 -sidebar_position: 5 -data: 2022年2月10日 +description: Python 的函数方法 +keywords: +- Python +- 函数方法 +tags: +- Python +sidebar_position: 4 +author: 7Wate +date: 2022-12-03 --- ## 函数 @@ -190,144 +196,6 @@ lambda [arg1 [,arg2,.....argn]]:expression 43 ``` -## 迭代器 - - 迭代器的使用非常普遍并使得 Python 成为一个统一的整体。 在幕后,for 语句会在容器对象上调用 `iter()`。 该函数返回一个定义了 `__next__()` 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,`__next__()` 将引发 `StopIteration` 异常来通知终止 `for` 循环。 可以使用 `next()` 内置函数来调用 __next__() 方法; - -- **iter()**:创建迭代器。 -- **next()**:输出迭代器的下一个元素。 - -```python -s = 'abc' -it = iter(s) - -print(it) # -next(it) # 'a' -next(it) # 'b' -next(it) # 'c' -next(it) - -# 抛出异常 -Traceback (most recent call last): - File "", line 1, in - next(it) -StopIteration -``` - -```python -# 为类添加迭代器 -class Reverse: - """Iterator for looping over a sequence backwards.""" - def __init__(self, data): - self.data = data - self.index = len(data) - - def __iter__(self): - return self - - def __next__(self): - if self.index == 0: - raise StopIteration - self.index = self.index - 1 - return self.data[self.index] -``` - -## yield 生成器 - -Python 中还有另外一种定义生成器的方式,就是通过`yield`关键字将一个普通函数改造成生成器函数。它们的写法类似于标准的函数,但当它们要返回数据时会使用 `yield` 语句。 - -```python -def fibonacci(n): # 生成器函数 - 斐波那契 - a, b, counter = 0, 1, 0 - while True: - if (counter > n): - return - yield a - a, b = b, a + b - counter += 1 - -for i in fibonacci(10): - print(i,end=" ") -``` - -## 推导生成式 - -Python 推导式是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体。 - -### 列表 - -```python -# 语法 -[out_exp_res for out_exp in input_list] -[out_exp_res for out_exp in input_list if condition] - -# 实例 -multiples = [i for i in range(30) if i % 3 == 0] -print(multiples) # [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] -``` - -### 字典 - -```python -# 语法 -{ key_expr: value_expr for value in collection } -{ key_expr: value_expr for value in collection if condition } - -# 实例 -dic = {x: x**2 for x in (2, 4, 6)} -print(dic) # {2: 4, 4: 16, 6: 36} -``` - -### 集合 - -```python -# 语法 -{ expression for item in Sequence } -{ expression for item in Sequence if conditional } - -# 实例 -a = {x for x in 'abracadabra' if x not in 'abc'} -print(a) # {'d', 'r'} -``` - -### 元组 - -```python -# 语法 -(expression for item in Sequence ) -(expression for item in Sequence if conditional ) - -# 实例 -a = (x for x in range(1,10)) -print(a) # 返回的是生成器对象 -tuple(a) # 使用 tuple() 函数,可以直接将生成器对象转换成元组 -``` - -### 其他 - -```python -f = [x for x in range(1, 10)] -print(f) - -f = [x + y for x in 'ABCDE' for y in '1234567'] -print(f) -# 用列表的生成表达式语法创建列表容器 -# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间 - -f = [x ** 2 for x in range(1, 1000)] -print(sys.getsizeof(f)) # 查看对象占用内存的字节数 -print(f) - -# 请注意下面的代码创建的不是一个列表而是一个生成器对象 -# 通过生成器可以获取到数据但它不占用额外的空间存储数据 -# 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间) -f = (x ** 2 for x in range(1, 1000)) -print(sys.getsizeof(f)) # 相比生成式生成器不占用存储数据的空间 -print(f) -for val in f: - print(val) -``` - ## 全局、局部变量 **定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。** diff --git a/wiki/programming-language/Python/入门/基础语法.md b/wiki/programming-language/Python/入门/基础语法.md index 038655b8..bcded518 100644 --- a/wiki/programming-language/Python/入门/基础语法.md +++ b/wiki/programming-language/Python/入门/基础语法.md @@ -13,7 +13,7 @@ date: 2022-11-20 ## 简介 -Python 是一门易于学习、功能强大的编程语言。同时是一种**解释型语言,不需要编译和链接**,可以节省大量开发时间。 +Python 是一门易于学习、功能强大的编程语言。同时是一种**解释型语言,不需要编译和链接**,可以节省大量开发时间。Python 入门相对其它程序设计语言简单,但是熟练掌握需要一定学习时间,尤其是丰富的魔术方法。 Python 程序**简洁、易读,通常**比实现同种功能的 C、C++、Java 代码短很多,原因如下: @@ -28,13 +28,13 @@ Python 官网上免费提供了 Python 解释器和扩展的标准库,包括 Python 官网还包含许多**免费丰富的第三方 Python 模块**、程序和工具发布包及文档链接。 -## 运行 +## 使用 ### 安装 Python 官网:,可以在[官方文档](https://docs.python.org/)帮助下选择合适版本安装。 -### 使用 +### 运行 Python 安装后在 Shell 中输入 Python 既可运行交互解释器,在解释器内编写 Python 脚本既可以运行。如果需要退出,输入`quit()`。 @@ -84,7 +84,7 @@ else: ## 行语句 -如果语句很长,我们可以使用反斜杠 **\\** 来实现多行语句;如果在同一行中使用多条语句,语句之间使用分号 **;** 分割。**在 [], {}, 或 () 中的多行语句,不需要使用反斜杠 \\。** +如果语句很长,我们可以使用反斜杠 **\\** 来实现多行语句;如果在同一行中使用多条语句,语句之间使用分号 **;** 分割。**在 [], {}, 或 () 中的多行语句,不需要使用反斜杠 \\。** 字符串之间不换行则会自动拼接。 ```python # 反斜杠 \ 用法 @@ -98,6 +98,9 @@ import sys; x = 'runoob'; sys.stdout.write(x + '\n') # 特殊多行语句 total = ['item_one', 'item_two', 'item_three', 'item_four', 'item_five'] + +# 字符串 +str = "hello" "world" "!" ``` ## 复合语句 diff --git a/wiki/programming-language/Python/入门/控制语句.md b/wiki/programming-language/Python/入门/控制语句.md index 2b36e82f..a35896cb 100644 --- a/wiki/programming-language/Python/入门/控制语句.md +++ b/wiki/programming-language/Python/入门/控制语句.md @@ -11,7 +11,12 @@ author: 7Wate date: 2022-11-19 --- -## 判断 +## 条件 + +在 Python 中,判断的值可以分为: + +- 假值 :None、空列表、空集合、空字典,空元组、空字符串、0、False 等。 +- 真值 :非空列表、非空集合、非空字典,非空元组、非空字符串、非 0 数值、True 等。 ### if @@ -69,9 +74,6 @@ Python 的 for 语句与 C 或 Pascal 中的不同。**Python 的 for 语句不 ```python """ 用for循环实现1~100求和 - -Version: 0.1 -Author: 骆昊 """ sum = 0 @@ -96,9 +98,6 @@ print(sum) ```python """ 猜数字游戏 - -Version: 0.1 -Author: 骆昊 """ import random @@ -213,7 +212,7 @@ except: ### with -Python 的 [`with`](https://docs.python.org/zh-cn/3/reference/compound_stmts.html#with) 语句支持通过上下文管理器所定义的运行时上下文这一概念。 此对象的实现使用了一对专门方法,**允许用户自定义类来定义运行时上下文**,在语句体被执行前进入该上下文,并在语句执行完毕时退出该上下文。 +Python 的 with 语句支持通过上下文管理器所定义的运行时上下文这一概念。 此对象的实现使用了一对专门方法,**允许用户自定义类来定义运行时上下文**,在语句体被执行前进入该上下文,并在语句执行完毕时退出该上下文。 通过上下文管理器,我们可以更好的控制对象在不同区间的特性,并且**可以使用 with 语句替代 try...except** 方法,使得代码更加的简洁,主要的**使用场景是访问资源,可以保证不管过程中是否发生错误或者异常都会执行相应的清理操作,释放出访问的资源。** @@ -263,7 +262,7 @@ NameError: HiThere ### assert -Python assert(断言)用于判断一个表达式,在表达式条件为 false 的时候触发异常。 +Python 断言(assert)用于判断一个表达式,在表达式条件为 false 的时候触发异常。 简单形式 `assert expression` 等价于: @@ -290,6 +289,10 @@ while True: # 最小的类 class MyEmptyClass: pass + +# 三个点 等同于 pass +class MyEmptyClass: + ... ``` ### del @@ -331,37 +334,3 @@ starting... 9 10 ``` - -### import - -基本的 import 语句(不带 from 子句)会分两步执行: - -1. 查找一个模块,如果有必要还会加载并初始化模块。 - -2. 在局部命名空间中为 import 语句发生位置所处的作用域定义一个或多个名称。 - -```python -import foo # foo imported and bound locally -import foo.bar.baz # foo.bar.baz imported, foo bound locally -import foo.bar.baz as fbb # foo.bar.baz imported and bound as fbb -from foo.bar import baz # foo.bar.baz imported and bound as baz -from foo import attr # foo imported and foo.attr bound as attr -``` - -from 形式使用的过程略微繁复一些: - -1. 查找 from 子句中指定的模块,如有必要还会加载并初始化模块; - -2. 对于 import 子句中指定的每个标识符: - 1. 检查被导入模块是否有该名称的属性 - 2. 如果没有,尝试导入具有该名称的子模块,然后再次检查被导入模块是否有该属性 - 3. 如果未找到该属性,则引发 ImportError。 - 4. 否则的话,将对该值的引用存入局部命名空间,如果有 as 子句则使用其指定的名称,否则使用该属性的名称 - -### global - -global 语句是作用于整个当前代码块的声明。 它意味着所列出的标识符将被解读为全局变量。 - -### nonlocal - -nonlocal 语句会使得所列出的名称指向之前在最近的包含作用域中绑定的除全局变量以外的变量。 diff --git a/wiki/programming-language/Python/入门/数据类型.md b/wiki/programming-language/Python/入门/数据类型.md index a70669b4..aa0f4156 100644 --- a/wiki/programming-language/Python/入门/数据类型.md +++ b/wiki/programming-language/Python/入门/数据类型.md @@ -5,7 +5,7 @@ keywords: - Python - 数据类型 tags: -- 标签 +- Python sidebar_position: 2 author: 7Wate date: 2022-11-19 @@ -13,6 +13,8 @@ date: 2022-11-19 ## 内置类型 +**Python中的一切都是对象,变量是对象的引用!同时 Python 的动态语言特性变量和常量不需要事先声明类型。** + **Python 3 内置类型**如下,除了各种数据类型,Python 解释器内建了还有很多其他类型,比如上下文管理器类型,模块、方法、代码对象、类型对象、内部对象等类型。 | 类型 | 可变性 | 描述 | 语法例子 | @@ -100,4 +102,5 @@ $ print(keyword.kwlist) - 受保护的实例属性用单个下划线开头(后面会讲到)。 - 私有的实例属性用两个下划线开头(后面会讲到)。 -使用 type() 检查变量的类型。 +Python 使用 `type() 检查变量的类型。 +Python 使用 `id() 查看变量的内存地址。 diff --git a/wiki/programming-language/Python/入门/数据结构.md b/wiki/programming-language/Python/入门/数据结构.md index 6dddb7cd..79c5fff6 100644 --- a/wiki/programming-language/Python/入门/数据结构.md +++ b/wiki/programming-language/Python/入门/数据结构.md @@ -6,14 +6,14 @@ keywords: - 数据结构 tags: - Python -sidebar_position: 4 +sidebar_position: 5 author: 7Wate date: 2022-11-20 --- -## 数字 +## 数值 -Python 数字数据类型用于存储数值。数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间。Python 支持三种不同的数值类型: +Python 数值数据类型用于存储数值。数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间。Python 支持三种不同的数值类型: - **整型(int)** - 通常被称为是整型或整数,是正或负整数,不带小数点。Python3 整型是没有限制大小的,可以当作 Long 类型使用,所以 Python3 没有 Python2 的 Long 类型。布尔(bool)是整型的子类型。 - **浮点型(float)** - 浮点型由整数部分与小数部分组成,浮点型也可以使用科学计数法表示(2.5e2 = 2.5 x 102 = 250) @@ -526,3 +526,151 @@ for q, a in zip(questions, answers): # What is your favorite color? It is blue. ``` +## 推导式 + +Python 推导式是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体。 + +### 列表 + +```python +# 语法 +[out_exp_res for out_exp in input_list] +[out_exp_res for out_exp in input_list if condition] + +# 实例 +multiples = [i for i in range(30) if i % 3 == 0] +print(multiples) # [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] +``` + +### 字典 + +```python +# 语法 +{ key_expr: value_expr for value in collection } +{ key_expr: value_expr for value in collection if condition } + +# 实例 +dic = {x: x**2 for x in (2, 4, 6)} +print(dic) # {2: 4, 4: 16, 6: 36} +``` + +### 集合 + +```python +# 语法 +{ expression for item in Sequence } +{ expression for item in Sequence if conditional } + +# 实例 +a = {x for x in 'abracadabra' if x not in 'abc'} +print(a) # {'d', 'r'} +``` + +### 元组 + +```python +# 语法 +(expression for item in Sequence ) +(expression for item in Sequence if conditional ) + +# 实例 +a = (x for x in range(1,10)) +print(a) # 返回的是生成器对象 +tuple(a) # 使用 tuple() 函数,可以直接将生成器对象转换成元组 +``` + +### 其他 + +```python +f = [x for x in range(1, 10)] +print(f) + +f = [x + y for x in 'ABCDE' for y in '1234567'] +print(f) +# 用列表的生成表达式语法创建列表容器 +# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间 + +f = [x ** 2 for x in range(1, 1000)] +print(sys.getsizeof(f)) # 查看对象占用内存的字节数 +print(f) + +# 请注意下面的代码创建的不是一个列表而是一个生成器对象 +# 通过生成器可以获取到数据但它不占用额外的空间存储数据 +# 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间) +f = (x ** 2 for x in range(1, 1000)) +print(sys.getsizeof(f)) # 相比生成式生成器不占用存储数据的空间 +print(f) +for val in f: + print(val) +``` + +## 迭代器 + +可以利用 for 循环的对象,都叫可迭代对象。列表、元组、字典、字符串等都是可迭代对象。 + +迭代器非常普遍的使用并使得 Python 成为一个统一的整体。 在幕后,for 语句会在容器对象上调用 `iter()`。 该函数返回一个定义了 `__next__()` 方法的迭代器对象,此方法将逐一访问容器中的元素。 当元素用尽时,`__next__()` 将引发 `StopIteration` 异常来通知终止 `for` 循环。 可以使用 `next()` 内置函数来调用 __next__() 方法; + +- **iter()**:创建迭代器。 +- **next()**:输出迭代器的下一个元素。 + +```python +s = 'abc' +it = iter(s) + +print(it) # +next(it) # 'a' +next(it) # 'b' +next(it) # 'c' +next(it) + +# 抛出异常 +Traceback (most recent call last): + File "", line 1, in + next(it) +StopIteration +``` + +```python +# 为类添加迭代器 +class Reverse: + """Iterator for looping over a sequence backwards.""" + def __init__(self, data): + self.data = data + self.index = len(data) + + def __iter__(self): + return self + + def __next__(self): + if self.index == 0: + raise StopIteration + self.index = self.index - 1 + return self.data[self.index] +``` + +## 生成器 + +生成器(Generator)是一个可以像迭代器那样使用 for 循环来获取元素的函数。 + +Python 中还有另外一种定义生成器的方式,就是通过 yield 关键字将一个普通函数改造成生成器函数。yield 相当于 return 但有所不同,yield 虽然返回了但是函数并没有结束。当函数运行到 yield 后,函数暂停运行并把 yield 后表达式的值返回出去。若 yield 没有接任何值,则返回 None。 + +生成器在其生命周期中,会有如下四个状态: + +- GEN_CREATED:生成器已创建,还未被激活。 +- GEN_RUNNING:解释器正在执行(只有在多线程应用中才能看到这个状态)。 +- GEN_SUSPENDED:在 yield 表达式处暂停。 +- GEN_CLOSED:生成器执行结束。 + +```python +def fibonacci(n): # 生成器函数 - 斐波那契 + a, b, counter = 0, 1, 0 + while True: + if (counter > n): + return + yield a + a, b = b, a + b + counter += 1 + +for i in fibonacci(10): + print(i,end=" ") +``` diff --git a/wiki/programming-language/Python/入门/模块和包.md b/wiki/programming-language/Python/入门/模块和包.md index e2da95f9..c9359f64 100644 --- a/wiki/programming-language/Python/入门/模块和包.md +++ b/wiki/programming-language/Python/入门/模块和包.md @@ -87,6 +87,22 @@ runoob2() Python 中,模块(包、类、函数)的导入方式有以下四种: +import 语句(不带 from 子句)会分两步执行: + +1. 查找一个模块,如果有必要还会加载并初始化模块。 + +2. 在局部命名空间中为 import 语句发生位置所处的作用域定义一个或多个名称。 + +from 形式使用的过程略微繁复一些: + +1. 查找 from 子句中指定的模块,如有必要还会加载并初始化模块; + +2. 对于 import 子句中指定的每个标识符: + 1. 检查被导入模块是否有该名称的属性 + 2. 如果没有,尝试导入具有该名称的子模块,然后再次检查被导入模块是否有该属性 + 3. 如果未找到该属性,则引发 ImportError。 + 4. 否则的话,将对该值的引用存入局部命名空间,如果有 as 子句则使用其指定的名称,否则使用该属性的名称 + ```python import xx.xx from xx.xx import xx diff --git a/wiki/programming-language/Python/入门/面对对象.md b/wiki/programming-language/Python/入门/面对对象.md index 8f53d1b1..0bfeb111 100644 --- a/wiki/programming-language/Python/入门/面对对象.md +++ b/wiki/programming-language/Python/入门/面对对象.md @@ -13,11 +13,11 @@ date: 2022-11-20 面对对象把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派 -## 类和对象 +## 类 -类是对象的蓝图和模板,而对象是类的实例。类是抽象的概念,而对象是具体的东西。 +类(class)是具有相同特性(属性)和行为(方法)的对象(实例)的抽象模板。 -在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做“类”的东西。 +在面向对象编程的世界中,一切皆为对象,对象都有属性和行为,每个对象都是独一无二的,而且对象一定属于某个类(型)。当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽取出来后,就可以定义出一个叫做类的东西。 - **类(Class):** 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。 - **方法:**类中定义的函数。 @@ -26,11 +26,11 @@ date: 2022-11-20 - **方法重写:**如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。 - **局部变量:**定义在方法中的变量,只作用于当前实例的类。 - **实例变量:**在类的声明中,属性是用变量来表示的,这种变量就称为实例变量,实例变量就是一个用 self 修饰的变量。 -- **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)。 +- **继承:**即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如有这样一个设计:一个 Dog 类型的对象派生自 Animal 类,这是模拟"是一个(is-a)"关系(例 Dog 是一个 Animal)。 - **实例化:**创建一个类的实例,类的具体对象。 - **对象:**通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。 -### 类定义 +### 定义 Python中可以使用`class`关键字定义类,然后在类中通过之前学习过的函数来定义方法,这样就可以将对象的动态特征描述出来,代码如下所示。 @@ -58,52 +58,92 @@ print("MyClass 类的属性 i 为:", x.i) print("MyClass 类的方法 f 输出为:", x.f()) ``` -### 类属性 +### 属性 属性引用使用和 Python 中所有的属性引用一样的标准语法:**obj.name**。类对象创建后,类命名空间中所有的命名都是有效属性名。 -### 类方法 +### 方法 -在类的内部,使用 **def** 关键字来定义一个方法,与一般函数定义不同,**类方法必须包含参数 self,** 且为第一个参数,self 代表的是类的实例。 +在类的内部,使用 **def** 关键字来定义一个方法,与一般函数定义不同,**类方法必须包含参数 self,** 且为第一个参数,self 代表的是类的实例方法。 ```python -#类定义 -class people: - #定义基本属性 - name = '' - age = 0 - #定义私有属性,私有属性在类外部无法直接进行访问 - __weight = 0 - #定义构造方法 - def __init__(self,n,a,w): - self.name = n - self.age = a - self.__weight = w - def speak(self): - print("%s 说: 我 %d 岁。" %(self.name,self.age)) - -# 实例化类 -p = people('runoob',10,30) -p.speak() # runoob 说: 我 10 岁。 +class Animal: + def __init__(self, name): + self.name = name + + def run(self): + print(f"{self.name}跑起来啦") + +>>> dog=Animal(name="小黑") +>>> dog.run() +小黑跑起来啦 +>>> Animal.run(dog) +小黑跑起来啦 +``` + +### 类方法 + +类方法在定义时,第一个参数固定是 cls,为 class 的简写,代表类本身。不管是通过实例还是类调用类方法,都不需要传入 cls 的参数。 + +```python +class Animal: + def __init__(self, name): + self.name = name + + def run(self): + print(f"{self.name}跑起来啦") + + @classmethod + def jump(cls, name): + print(f"{name}跳起来啦") + +>>> dog=Animal(name="小黑") +>>> dog.eat() +正在吃饭... +>>> Animal.eat() +正在吃饭... +``` + +### 静态方法 + +Python 类的静态方法在定义时,不需要 self 参数。 @staticmethod 装饰的函数就是静态方法,静态方法不需要实例化就可以调用。 + +```python +class Animal: + def __init__(self, name): + self.name = name + + def run(self): + print(f"{self.name}跑起来啦") + + @staticmethod + def eat(): + print("正在吃饭...") + +>>> dog=Animal(name="小黑") +>>> dog.jump("小黑") +小黑跳起来啦 +>>> Animal.jump("小黑") +小黑跳起来啦 ``` ### 访问可见性 -因为在很多面向对象编程语言中,我们通常会将对象的属性设置为私有的(private)或受保护的(protected),简单的说就是不允许外界访问,而对象的方法通常都是公开的(public),因为公开的方法就是对象能够接受的消息。 +在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的。如果希望属性是私有的,在给属性命名时可以参考以下方法 -在Python中,属性和方法的访问权限只有两种,也就是公开的和私有的,如果希望属性是私有的,在给属性命名时可以用两个下划线作为开头。 +#### 私有属性、方法 -#### 私有属性 +##### 单下划线 -**__private_attrs**:以两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 **self.__private_attrs**。 +Python 以单个下划线开头的变量或方法仅供内部使用,但是不做强制约束,依旧可以正常访问。 -#### 私有方法 +##### 双下划线 -**__private_method**:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,不能在类的外部调用。在类内部的方法中使用时**self.__private_methods**。 +Python 以两个下划线开头会导致 Python解释器重写属性名称,以避免子类中的命名冲突 #### 强制访问 -但是,Python并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。 +但是,Python 并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们,下面的代码就可以验证这一点。 ```python class Test: @@ -126,30 +166,41 @@ if __name__ == "__main__": main() ``` -### 专有方法 +### 封装 + +封装是指将数据与具体操作的实现代码放在某个对象内部,使这些代码的实现细节不被外界发现,外界只能通过接口使用该对象,而不能通过任何形式修改对象内部实现。 ```python -__init__ : 构造函数,在生成对象时调用 -__del__ : 析构函数,释放对象时使用 -__repr__ : 打印,转换 -__setitem__ : 按照索引赋值 -__getitem__ : 按照索引获取值 -__len__ : 获得长度 -__cmp__ : 比较运算 -__call__ : 函数调用 -__add__ : 加运算 -__sub__ : 减运算 -__mul__ : 乘运算 -__truediv__ : 除运算 -__mod__ : 求余运算 -__pow__ : 乘方 +############ 未封装 +class Person: + def __init__(self, name, age): + self.name = name + self.age = age + +xh = Person(name="小红", age=27) +if xh.age >= 18: + print(f"{xh.name}已经是成年人了") +else: + print(f"{xh.name}还是未年人") + +############ 封装后 +class Person: + def __init__(self, name, age): + self.name = name + self.__age = age + + def is_adult(self): + return self.__age >= 18 + +xh = Person(name="小红", age=27) +xh.is_adult() ``` ### 继承 -面对对象的三大特征其一继承:可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。 +继承可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。 -子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为[里氏替换原则](https://zh.wikipedia.org/wiki/里氏替换原则)。下面我们先看一个继承的例子。 +子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则。下面我们先看一个继承的例子。 ```python # 语法 @@ -260,196 +311,10 @@ c.myMethod() # 子类调用重写方法 super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法 ``` -### 类的关系 +### 关系 简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。 - is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。 - has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。 - use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。 - -## 面向对象进阶 - -### **slots** 魔法 - -如果我们需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效,对子类并不起任何作用。 - -```python -class Person(object): - - # 限定Person对象只能绑定_name, _age和_gender属性 - __slots__ = ('_name', '_age', '_gender') - - def __init__(self, name, age): - self._name = name - self._age = age - - @property - def name(self): - return self._name - - @property - def age(self): - return self._age - - @age.setter - def age(self, age): - self._age = age - - def play(self): - if self._age <= 16: - print('%s正在玩飞行棋.' % self._name) - else: - print('%s正在玩斗地主.' % self._name) - - -def main(): - person = Person('王大锤', 22) - person.play() - person._gender = '男' - # AttributeError: 'Person' object has no attribute '_is_gay' - # person._is_gay = True -``` - -### @property 装饰器 - -如果想访问属性可以通过属性的 getter(访问器)和 setter(修改器)方法进行对应的操作。要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下所示。 - -```python -class Person(object): - - def __init__(self, name, age): - self._name = name - self._age = age - - # 访问器 - getter方法 - @property - def name(self): - return self._name - - # 访问器 - getter方法 - @property - def age(self): - return self._age - - # 修改器 - setter方法 - @age.setter - def age(self, age): - self._age = age - - def play(self): - if self._age <= 16: - print('%s正在玩飞行棋.' % self._name) - else: - print('%s正在玩斗地主.' % self._name) - - -def main(): - person = Person('王大锤', 12) - person.play() - person.age = 22 - person.play() - # person.name = '白元芳' # AttributeError: can't set attribute - - -if __name__ == '__main__': - main() -``` - -### @staticmethod 静态方法 - -可以使用`@staticmethod`注解定义静态方法,通过直接调用类使用方法。 - -```python -from math import sqrt - - -class Triangle(object): - - def __init__(self, a, b, c): - self._a = a - self._b = b - self._c = c - - @staticmethod - def is_valid(a, b, c): - return a + b > c and b + c > a and a + c > b - - def perimeter(self): - return self._a + self._b + self._c - - def area(self): - half = self.perimeter() / 2 - return sqrt(half * (half - self._a) * - (half - self._b) * (half - self._c)) - - -def main(): - a, b, c = 3, 4, 5 - # 静态方法和类方法都是通过给类发消息来调用的 - if Triangle.is_valid(a, b, c): - t = Triangle(a, b, c) - print(t.perimeter()) - # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 - # print(Triangle.perimeter(t)) - print(t.area()) - # print(Triangle.area(t)) - else: - print('无法构成三角形.') - - -if __name__ == '__main__': - main() -``` - -### @classmethod 类方法 - -Python 还可以在类中定义类方法,类方法的第一个参数约定名为 cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。 - -```python -from time import time, localtime, sleep - - -class Clock(object): - """数字时钟""" - - def __init__(self, hour=0, minute=0, second=0): - self._hour = hour - self._minute = minute - self._second = second - - @classmethod - def now(cls): - ctime = localtime(time()) - return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec) - - def run(self): - """走字""" - self._second += 1 - if self._second == 60: - self._second = 0 - self._minute += 1 - if self._minute == 60: - self._minute = 0 - self._hour += 1 - if self._hour == 24: - self._hour = 0 - - def show(self): - """显示时间""" - return '%02d:%02d:%02d' % \ - (self._hour, self._minute, self._second) - - -def main(): - # 通过类方法创建对象并获取系统时间 - clock = Clock.now() - while True: - print(clock.show()) - sleep(1) - clock.run() - - -if __name__ == '__main__': - main() -```