返回列表 发帖

1:Python 两大特性和四大基本语法

Day 1:Python 两大特性和四大基本语法

Python 语言使用率越来越高,使用 Python 不仅可以做 GUI 开发、Web 开发,还能进行数据预处理、数据探索性分析(EDA),更是进行数据挖掘、机器学习、深度学习等的首选语言。

基于 Python 的包更是枝繁叶茂,遍地开花,“Tiobe 编程语言排行榜”最新统计显示 Python 是增长最快的语言。

image-20200131192231967

这得益于 Python 语言语法简单、开发效率高,集成系统非常方便。

Python 相关的就业机会也是非常多,待遇也很优厚。

因此,不管从易用性,还是工作机会和待遇来说,Python 都是 IT 从业者需要掌握的语言。

接下来,与大家,还有远在美国做 AI 博士后研究的 Alicia,一起开始我们的 60 天 Python 探索之旅吧。

开始前,先了解下这个专栏的基本使用说明,主要包括如下几点:

使用的是 Python 3。
被讨论到的每个知识点都配备有小案例,辅助大家快速理解知识点,同时加深印象。
为了学习方便,对于小的代码块,尽量使用 IPython 或 Jupyter Notebook 交互工具做演示。
对于专栏涉及到的实战项目,相应的代码会按照软件工程和设计模式的思想,去拆分和组织。
书写的语言尽量做到通俗易懂,不搞华丽辞藻。
所有的这些考虑,都是为了让大家在短时间内掌握 Python 技术栈,多一个生存的本领。拿到理想的 Offer 后,早日过上自己想要的生活。

让我们开始吧。

首先问大家一个问题,你知道 Python 是一门什么样的语言吗?

Python 语言两大特性
Python 是一门动态的、强类型语言。

什么是动态语言?
要了解什么是动态语言,要首先了解“类型检查”。

类型检查是验证类型约束的过程,编译器或解释器通常在编译阶段或运行阶段做类型检查。

类型检查就是查看“变量”和它们的”类型”,然后判断表达式是否合理。例如,不能拿一个 string 类型变量除以浮点数变量。

如果类型检查发生在程序运行阶段(run time),那么它便是“动态类型语言”(dynamically typed languages)。常见的动态语言包括:

Python
JavaScrpit
PHP
类型检查发生在“编译阶段”(compile time)的是“静态类型语言”(statically typed languages)。常见的静态类型语言包括:

C
C++
Java
C#
Scala
什么是强类型语言?
强类型语言是指:不管是在编译阶段还是运行阶段,一旦某种类型绑定到变量后,此变量便会持有此类型,并且不能同其他类型在计算表达式时,混合使用。

例如,在交互式工具 IPython 中输入如下两行代码:

In [1]: a = 5
In [2]: a = a + 's'
程序会抛出 TypeError 异常:

unsupported operand type(s) for +: 'int' and 'str'
意思是不支持 int 变量和 str 变量相加。

常见的强类型语言有:

Python
Java
C#
Scala
与之对应的是弱类型语言,弱类型语言容易与其他类型混合计算。弱类型语言代表 JavaScript。

支持如下操作:

var data = 5
data = data + 'xiaoming' //string 和 int 结合自动转化为 string
常见的弱类型语言有:

C
C++
PHP
Javascript
如下,按照是否为静态/动态语言,弱类型/强类型两个维度,总结常用的语言分类。

image-20200205155429583

四大基本语法
分别从变量命名规则、缩进原则、特殊关键字和特殊运算符四个方面,总结 Python 的基本语法。

命名规则
Python 的变量命名规则主要包括两条:

允许包括英文、数字以及下划线(_),不能以数字开头
名称区分大小写
特别说明以“下划线”开头的变量是有特殊意义的:

类变量若以单下划线(_)开头,代表不能直接被访问,类似于 C# 的受保护型变量(protected),表示不能通过 import module_name 而导入。
类变量若以双下划(__)开头,表示为类的私有成员,不能被导入和其他类变量访问。
以双下划开头和双下划线结尾的变量是 Python 里的专用标识,有特殊的身份。
如 Python 自定义类中都包括 __init__ 和 __add__ 方法,如果不重写 __add__ 去执行两个类加法操作,程序会抛 TypeError 异常。只有重写后,程序才能正常执行加法操作。

Python 变量命名习惯一般遵守蛇形命名法(snake case):

一般变量命名,book_id、book_store_count;
类名首字符为大写,如 Python 内置模块 collections.abc 中的 Iterable 类、我们自定义的 Book 类等;
类方法名:get_store_count();
其他特殊变量,会全部大写,M_PI、MAX_VEHICLE_SPEED。
这与 Java 命名方法不同,Java 最典型的命名方法——驼峰命名法(camel case)。

缩进原则
Python 最具特色的地方就是用缩进代替 Java、C++ 中的 {},缩进的层级结构表示代码的逻辑层次。

比如,自定义一个 Book 类,重写 __add__ 方法计算两类书的库存量和。

Python 的缩进方法,一般为 4 个字符。

代码行 class Book(object) 与代码行 # 定义类的参数 的缩进,此处为 4 个字符;
代码行 def __add__(self,book): 与 return 所在行缩进也是 4 个字符。
通过这种层级结构,展现代码的逻辑层次。

下面代码,创建一个 Book 类:

class Book(object):
    # 定义类的参数
    def __init__(self,book_id, book_name, book_store_count):
        self.book_id = book_id
        self.book_name = book_name
        self.book_store_count = book_store_count
    # 重写加法操作   
    def __add__(self,book):
        return self.book_store_count + book.book_store_count

# 创建两个 Book 类的实例:
python_intro_book = Book(1,'python入门书',100)
ml_intro_book = Book(2,'机器学习入门书',200)
# 求两本书的总销量
sales_cnt = python_intro_book + ml_intro_book
print(sales_cnt) # 300
如下是代码执行结果的演示图,打印总销量 300。结合图形,辅助大家快速理解代码。

image-20200218101020064

为了帮助新手更容易理解代码整个执行过程,专栏会配备相应的演示动画:



缩进格式、行间空行数、变量和等号空格等 Python 编码规范参考PEP8。

autopep8 包遵循 PEP8 的所有规范,安装此包,做好相关配置,便可自动实现 PEP8 制定的编码规范。

特殊关键字
Python 有 35 个关键字:

False      await      else       import     pass
None       break      except     in         raise
True       class      finally    is         return
and        continue   for        lambda     try
as         def        from       nonlocal   while
assert     del        global     not        with
async      elif       if         or         yield
自定义变量名不能与它们重复。

常用且不同于其他常用语言 C++ 和 Java 的关键字,如:

True 和 False 用于表示值的真假,在 Java 中是 true 和 false;
逻辑反操作 Python 使用 not,Java 是 !;
None 表示空值,Java 使用 null;
Python 两个条件同时满足使用 and,Java 是 &&;
两者满足其一,Python 使用 or,Java 使用 ||;
Python 使用 elif, Java 是 else if。
其他比较特殊的关键字,如:

del 用于删除可迭代对象中某个元素;
def 用于定义函数;
带 yield 用于定义生成器(generator)函数;
global 和 nonlocal 一种应用是 Python 函数式编程的闭包场景;
pass 一种应用是定义接口,也是 Python 语言特有的一个关键字。
这些关键字的用法,会在后续文章,更为详细的介绍。在此,先构建一个整体上的认识即可。

特殊运算符
Python 的运算符包括:

+       -       *       **      /       //      %      @
<<      >>      &       |       ^       ~       :=
<       >       <=      >=      ==      !=
大部分运算符应该被熟知,重点介绍 3 个比较特殊的://、**、:=。

// 用于两个数值相除且向下取整,与 Python 的 math 模块中 floor 功能相似:

In [1]: 5//2
Out[1]: 2
In [2]: 5//4.5
Out[2]: 1.0
** 用于幂运算:

In [1]: 2**3
Out[1]: 8
:= 是在 2019 年,Python 3.8 版本里,刚刚才被支持的运算符,被形象地称为“海象运算符”。

n = len(a)
if n > 10:
    print(f"{n}大于10")
如果使用“海象运算符”,写法上更为精简:

if (n := len(a)) > 10:
    print(f"{n}大于10")
Python 比较运算符还支持链式比较,应用起来更加方便,比如:

i = 3
print(1 < i < 3) # False
print(1 < i <= 3) # True
另外,运算符 @ 用于装饰器功能,本专栏会深入解释它的本质,同时配备的几个相关案例,一定会帮助你学会使用装饰器。

小结
Python 学习第一天,首先认识 Python 两大特征:

动态语言:动态指代码运行时才被编译器一行一行翻译执行;
强类型:强类型指被绑定一个类型后便不能修改,不能与其他类型混用。
四大基本语法,总结了 Python 的命名规则、缩进原则、特殊关键字、特殊运算符,为后面的学习打下基础。

评论写评论

Charon15 小时前
打卡
鼓掌

张鹏21 小时前
打卡
鼓掌

三哥2 天前
打卡
鼓掌

古塔山4 天前
打卡
鼓掌

zglg(作者)3 天前
坚持下去,希望看到你在第二天的打卡
鼓掌

AI悦创4 天前
写的一般,就这样
1

zglg(作者)3 天前
谢谢鼓励
鼓掌

陶涛4 天前
定义类的时候为什么要重写加法之类的操作?以及还有情况下需要重写?
鼓掌

zglg(作者)3 天前
个性化两个类实例add的行为,比如,__add__ 定义为出两类书的销量和。
鼓掌

望云5 天前
每天至少打卡一个钟。2020.2.16
3

神经蛙5 天前
打卡
2

canlan6 天前
打卡1
3

李金龙6 天前
C和C++是静态弱类型语言,能在稍微解释一下吗,是因为它们有隐是类型转换吗?还有就是感觉C更弱一些,文章中的配图里面看C++更靠近弱类型,能给一下原因吗?
1

zglg(作者)3 天前
弱类型语言支持:1+'s' 操作;典型的javascript; 配图横轴下都是弱类型,没有哪个更靠近弱类型,要么是要么不是,没有中间状态
鼓掌

openbilibili6 天前
C和C++没有动静态之分 而且静态和动态的区分要给一个标准
鼓掌

食尚 - 梁升源7 天前
mark 1.
1

侯子7 天前
学习打卡 day 1
1

高天杰8 天前
Day1,虽然有的地方还不是很理解,但是老师描述的基本都能看懂
1

zglg(作者)3 天前
过几天再来看看,兴许就懂了。 今天,我又添加代码执行过程的动画演示,帮助大家更好理解代码。
鼓掌

Pending8 天前
python2还是3
1

三哥2 天前
python2马上都不维护了
鼓掌

zglg(作者)7 天前
python3
鼓掌

Big Heart8 天前
1
1

飞哥传说Axe8 天前
开始学习
1

河外纪元8 天前
很好很好
2

箫松彧8 天前
def __init__(self,book_id, book_name, book_store_count),请问这条语句里面的self是什么意思?
1

zglg(作者)8 天前
self是自定义类,本身。
鼓掌

@小付@8 天前
每天打卡。
1

守望者9 天前
我最喜欢的一点就是讲四大基本语法的时候,有和Java的语法、使用习惯相比较,对于学了Java的人来说不仅能学到Python,还能了解各种语言的特点和界限。
3

zglg(作者)9 天前
python, java, js都是目前应用较多的语言,对于了解java的小伙伴,通过这个专栏,因为都有很多案例,相信会很快入门和掌握python技术栈
1

&#128033;赵小胖10 天前
写的很详细哇~ 那句话很好:别的老师在介绍知识点时都会说“这东西是什么”,但我不想这样做。我觉得“为什么这东西是这样”或者“在什么场景适应什么需求有什么好处才会用这东西”。
2

zglg(作者)9 天前
谢谢赞美,确实是这样。要想讲清楚,还真不能只讲what,还得多探索why, how apply等等~~
2
限时特价¥49
写评论...
分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友

回复 2# network
Python 四大数据类型总结
基本数据类型

数值型

Python 中的数据皆是对象,比如被熟知的 int 整型对象、float 双精度浮点型、bool 逻辑对象,它们都是单个元素。举两个例子。

前缀加 0x,创建一个十六进制的整数:

0xa5 # 等于十进制的 165
使用 e 创建科学计数法表示的浮点数:

1.05e3 # 1050.0
容器型

可容纳多个元素的容器对象,常用的比如:list 列表对象、 tuple 元组对象、dict 字典对象、set 集合对象。Python 定义这些类型的变量,语法非常简洁。

举例如下。

使用一对中括号 [],创建一个 list 型变量:

lst = [1,3,5] # list 变量
示意图看出,右侧容器为开环的,意味着可以向容器中增加和删除元素:

image-20200218111947234

使用一对括号 (),创建一个 tuple 型对象:

tup = (1,3,5) # tuple 变量
示意图看出,右侧容器为闭合的,意味着一旦创建元组后,便不能再向容器中增删元素:

image-20200218112031346

但需要注意,含单个元素的元组后面必须保留一个逗号,才被解释为元组。

tup = (1,) # 必须保留逗号
否则会被认为元素本身:

In [14]: tup=(1)
    ...: print(type(tup))
<class 'int'>
使用一对花括号 {} 另使用冒号 :,创建一个 dict 对象:

dic = {'a':1, 'b':3, 'c':5} # dict变量
字典是一个哈希表,下面的示意图形象的表达出字典的 “形”。

image-20200218112256968

仅使用一对花括号 {},创建一个 set 对象:

s = {1,3,5} # 集合变量
Python 的容器类型,list、dict、tuple、set 等能方便地实现强大的功能,下面给出几个案例。

1. 去最求平均

去掉列表中的一个最小值和一个最大值后,计算剩余元素的平均值。

def score_mean(lst):
    lst.sort()
    lst2=lst[1:-1]
    return round((sum(lst2)/len(lst2)),1)

lst=[9.1, 9.0,8.1, 9.7, 19,8.2, 8.6,9.8]
score_mean(lst) # 9.1
代码执行过程,动画演示:

image-20200218112655335

2. 打印 99 乘法表

打印出如下格式的乘法表:

1*1=1
1*2=2   2*2=4
1*3=3   2*3=6   3*3=9
1*4=4   2*4=8   3*4=12  4*4=16
1*5=5   2*5=10  3*5=15  4*5=20  5*5=25
1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36
1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49
1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64
1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81
一共有 10 行,第 i 行的第 j 列等于:j*i,其中:

i 取值范围:1<=i<=9
j 取值范围:1<=j<=i
根据“例子分析”的语言描述,转化为如下代码:

In [13]: for i in range(1,10):
    ...:     for j in range(1,i+1):
    ...:         print('%d*%d=%d'%(j,i,j*i),end='\t')
    ...:     print()
3. 样本抽样

使用 sample 抽样,如下例子从 100 个样本中随机抽样 10 个。

from random import randint,sample
lst = [randint(0,50) for _ in range(100)]
print(lst[:5])# [38, 19, 11, 3, 6]
lst_sample = sample(lst,10)
print(lst_sample) # [33, 40, 35, 49, 24, 15, 48, 29, 37, 24]
字符串

注意 Python 中没有像 C++ 表示的字符类型(char),所有的字符或串都被统一为 str 对象。如单个字符 c 的类型也为 str。

str 类型会被经常使用,先列举 5 个被高频使用的方法。

strip 用于去除字符串前后的空格:

In [1]: '  I love python\t\n  '.strip()
Out[1]: 'I love python'
replace 用于字符串的替换:

In [2]: 'i love python'.replace(' ','_')
Out[2]: 'i_love_python'
join 用于合并字符串:

In [3]: '_'.join(['book', 'store','count'])
Out[3]: 'book_store_count'
title 用于单词的首字符大写:

In [4]: 'i love python'.title()
Out[4]: 'I Love Python'
find 用于返回匹配字符串的起始位置索引:

In [5]: 'i love python'.find('python')
Out[5]: 7
举个应用字符串的案例,判断 str1 是否由 str2 旋转而来。

字符串 stringbook 旋转后得到 bookstring,写一段代码验证 str1 是否为 str2 旋转得到。

转化为判断:str1 是否为 str2+str2 的子串。

下面函数原型中,注明了每个参数的类型、返回值的类型,增强代码的可读性和可维护性。

def is_rotation(s1: str, s2: str) -> bool:
    if s1 is None or s2 is None:
        return False
    if len(s1) != len(s2):
        return False

    def is_substring(s1: str, s2: str) -> bool:
        return s1 in s2
    return is_substring(s1, s2 + s2)
测试函数 is_rotation:

r = is_rotation('stringbook', 'bookstring')
print(r)  # True

r = is_rotation('greatman', 'maneatgr')
print(r)  # False
代码执行过程,动画演示:



字符串的匹配操作除了使用 str 封装的方法外,Python 的 re 正则模块功能更加强大,写法更为简便,广泛适用于爬虫、数据分析等。

下面这个案例实现:密码安全检查,使用正则表达式非常容易实现。

密码安全要求:

要求密码为 6 到 20 位;
密码只包含英文字母和数字。
import re
pat = re.compile(r'\w{6,20}') # 这是错误的,因为 \w 通配符匹配的是字母,数字和下划线,题目要求不能含有下划线
# 使用最稳的方法:\da-zA-Z 满足“密码只包含英文字母和数字”
# \d匹配数字 0-9
# a-z 匹配所有小写字符;A-Z 匹配所有大写字符
pat = re.compile(r'[\da-zA-Z]{6,20}')
选用最保险的 fullmatch 方法,查看是否整个字符串都匹配。

以下测试例子都返回 None,原因都在解释里。

pat.fullmatch('qaz12') # 返回 None,长度小于 6
pat.fullmatch('qaz12wsxedcrfvtgb67890942234343434') # None 长度大于 22
pat.fullmatch('qaz_231') # None 含有下划线
下面这个字符串 n0passw0Rd 完全符合:

In [20]: pat.fullmatch('n0passw0Rd')
Out[20]: <re.Match object; span=(0, 10), match='n0passw0Rd'>
自定义类型

Python 使用关键字 class 定制自己的类,self 表示类实例对象本身。

一个自定义类内包括属性、方法,其中有些方法是自带的。

类(对象):

class Dog(object):
    pass
以上定义一个 Dog 对象,它继承于根类 object,pass 表示没有自定义任何属性和方法。

下面创建一个 Dog 类型的实例:

wangwang = Dog()
Dog 类现在没有定义任何方法,但是刚才说了,它会有自带的方法,使用 __dir__() 查看这些自带方法:

In [26]: wangwang.__dir__()
Out[26]:
['__module__',
'__dict__',
'__weakref__',
'__doc__',
'__repr__',
'__hash__',
'__str__',
'__getattribute__',
'__setattr__',
'__delattr__',
'__lt__',
'__le__',
'__eq__',
'__ne__',
'__gt__',
'__ge__',
'__init__',
'__new__',
'__reduce_ex__',
'__reduce__',
'__subclasshook__',
'__init_subclass__',
'__format__',
'__sizeof__',
'__dir__',
'__class__']
有些地方称以上方法为魔法方法,它们与创建类时自定义个性化行为有关。比如:

__init__ 方法能定义一个带参数的类;
__new__ 方法自定义实例化类的行为;
__getattribute__ 方法自定义读取属性的行为;
__setattr__ 自定义赋值与修改属性时的行为。
类的属性:

def __init__(self, name, dtype):
     self.name = name
     self.dtype = dtype
通过 __init__,定义 Dog 对象的两个属性:name、dtype。

类的实例:

wangwang = Dog('wangwang','cute_type')
wangwang 是 Dog 类的实例。

类的方法:

def shout(self):
    print('I\'m %s, type: %s' % (self.name, self.dtype))
注意:

自定义方法的第一个参数必须是 self,它指向实例本身,如 Dog 类型的实例 dog;
引用属性时,必须前面添加 self,比如 self.name 等。
总结以上代码:

In [40]: class Dog(object):
    ...:     def __init__(self,name,dtype):
    ...:         self.name=name
    ...:         self.dtype=dtype
    ...:     def shout(self):
    ...:         print('I\'m %s, type: %s' % (self.name, self.dtype))

In [41]: wangwang = Dog('wangwang','cute_type')

In [42]: wangwang.name
Out[42]: 'wangwang'

In [43]: wangwang.dtype
Out[43]: 'cute_type'

In [44]: wangwang.shout()
I'm wangwang, type: cute_type
看到创建的两个属性和一个方法都被暴露在外面,可被 wangwang 调用。这样的话,这些属性就会被任意修改:

In [49]: wangwang.name='wrong_name'

In [50]: wangwang.name
Out[50]: 'wrong_name'
如果想避免属性 name 被修改,可以将它变为私有变量。改动方法:属性前加 2 个 _ 后,变为私有属性。如:

In [51]: class Dog(object):
    ...:     def __init__(self,name,dtype):
    ...:         self.__name=name
    ...:         self.__dtype=dtype
    ...:     def shout(self):
    ...:         print('I\'m %s, type: %s' % (self.name, self.dtype))
同理,方法前加 2 个 _ 后,方法变为“私有方法”,只能在 Dog 类内被共享使用。

但是这样改动后,属性 name 不能被访问了,也就无法得知 wangwang 的名字叫啥。不过,这个问题有一种简单的解决方法,直接新定义一个方法就行:

def get_name(self):
    return self.__name
综合代码:

In [52]: class Dog(object):
    ...:     def __init__(self,name,dtype):
    ...:         self.__name=name
    ...:         self.__dtype=dtype
    ...:     def shout(self):
    ...:         print('I\'m %s, type: %s' % (self.name, self.dtype))
    ...:     def get_name(self):
    ...:         return self.__name
    ...:

In [53]: wangwang = Dog('wangwang','cute_type')

In [54]: wangwang.get_name()
Out[54]: 'wangwang'
但是,通过此机制,改变属性的可读性或可写性,怎么看都不太优雅!因为无形中增加一些冗余的方法,如 get_name。

下面,通过另一个例子,解释如何更优雅地改变某个属性为只读或只写。

自定义一个最精简的 Book 类,它继承于系统的根类 object:

class Book(object):
    def __init__(self,name,sale):
        self.__name = name
        self.__sale = sale
使用 Python 自带的 property 类,就会优雅地将 name 变为只读的。

    @property
    def name(self):
        return self.__name
使用 @property 装饰后 name 变为属性,意味着 .name 就会返回这本书的名字,而不是通过 .name() 这种函数调用的方法。这样变为真正的属性后,可读性更好。

In [101]: class Book(object):
     ...:     def __init__(self,name,sale):
     ...:         self.__name = name
     ...:         self.__sale = sale
     ...:     @property
     ...:     def name(self):
     ...:         return self.__name

In [102]: a_book = Book('magic_book',100000)

In [103]: a_book.name
Out[103]: 'magic_book'
property 是 Python 自带的类,前三个参数都是函数类型。更加详细的讨论放在后面讨论装饰器时再展开。

In [104]: help(property)
Help on class property in module builtins:

class property(object)
|  property(fget=None, fset=None, fdel=None, doc=None)
如果使 name 既可读又可写,就再增加一个装饰器 @name.setter。

In [105]: class Book(object):
     ...:     def __init__(self,name,sale):
     ...:         self.__name = name
     ...:         self.__sale = sale
     ...:     @property
     ...:     def name(self):
     ...:         return self.__name
     ...:     @name.setter
     ...:     def name(self,new_name):
     ...:         self.__name = new_name

In [106]: a_book = Book('magic_book',100000)

In [107]: a_book.name = 'magic_book_2.0'

In [108]: a_book.name
Out[108]: 'magic_book_2.0'
注意这种装饰器写法:name.setter,name 已经被包装为 property 实例,调用实例上的 setter 函数再包装 name 后就会可写。对于 Python 入门者,可以暂时不用太纠结这部分理论,使用 Python 一段时间后,再回过头来自然就会理解。

小结

今天学习 Python 的四大基本数据类型。数值型 int、float 等;容器型 list、dict、tuple、set 等;字符型 str 与正则表达式介绍;自定义类的基本语法规则,class、属性和方法等。

TOP

Python 四大数据类型总结
基本数据类型

数值型

Python 中的数据皆是对象,比如被熟知的 int 整型对象、float 双精度浮点型、bool 逻辑对象,它们都是单个元素。举两个例子。

前缀加 0x,创建一个十六进制的整数:

0xa5 # 等于十进制的 165
使用 e 创建科学计数法表示的浮点数:

1.05e3 # 1050.0
容器型

可容纳多个元素的容器对象,常用的比如:list 列表对象、 tuple 元组对象、dict 字典对象、set 集合对象。Python 定义这些类型的变量,语法非常简洁。

举例如下。

使用一对中括号 [],创建一个 list 型变量:

lst = [1,3,5] # list 变量
示意图看出,右侧容器为开环的,意味着可以向容器中增加和删除元素:

image-20200218111947234

使用一对括号 (),创建一个 tuple 型对象:

tup = (1,3,5) # tuple 变量
示意图看出,右侧容器为闭合的,意味着一旦创建元组后,便不能再向容器中增删元素:

image-20200218112031346

但需要注意,含单个元素的元组后面必须保留一个逗号,才被解释为元组。

tup = (1,) # 必须保留逗号
否则会被认为元素本身:

In [14]: tup=(1)
    ...: print(type(tup))
<class 'int'>
使用一对花括号 {} 另使用冒号 :,创建一个 dict 对象:

dic = {'a':1, 'b':3, 'c':5} # dict变量
字典是一个哈希表,下面的示意图形象的表达出字典的 “形”。

image-20200218112256968

仅使用一对花括号 {},创建一个 set 对象:

s = {1,3,5} # 集合变量
Python 的容器类型,list、dict、tuple、set 等能方便地实现强大的功能,下面给出几个案例。

1. 去最求平均

去掉列表中的一个最小值和一个最大值后,计算剩余元素的平均值。

def score_mean(lst):
    lst.sort()
    lst2=lst[1:-1]
    return round((sum(lst2)/len(lst2)),1)

lst=[9.1, 9.0,8.1, 9.7, 19,8.2, 8.6,9.8]
score_mean(lst) # 9.1
代码执行过程,动画演示:

image-20200218112655335

2. 打印 99 乘法表

打印出如下格式的乘法表:

1*1=1
1*2=2   2*2=4
1*3=3   2*3=6   3*3=9
1*4=4   2*4=8   3*4=12  4*4=16
1*5=5   2*5=10  3*5=15  4*5=20  5*5=25
1*6=6   2*6=12  3*6=18  4*6=24  5*6=30  6*6=36
1*7=7   2*7=14  3*7=21  4*7=28  5*7=35  6*7=42  7*7=49
1*8=8   2*8=16  3*8=24  4*8=32  5*8=40  6*8=48  7*8=56  8*8=64
1*9=9   2*9=18  3*9=27  4*9=36  5*9=45  6*9=54  7*9=63  8*9=72  9*9=81
一共有 10 行,第 i 行的第 j 列等于:j*i,其中:

i 取值范围:1<=i<=9
j 取值范围:1<=j<=i
根据“例子分析”的语言描述,转化为如下代码:

In [13]: for i in range(1,10):
    ...:     for j in range(1,i+1):
    ...:         print('%d*%d=%d'%(j,i,j*i),end='\t')
    ...:     print()
3. 样本抽样

使用 sample 抽样,如下例子从 100 个样本中随机抽样 10 个。

from random import randint,sample
lst = [randint(0,50) for _ in range(100)]
print(lst[:5])# [38, 19, 11, 3, 6]
lst_sample = sample(lst,10)
print(lst_sample) # [33, 40, 35, 49, 24, 15, 48, 29, 37, 24]
字符串

注意 Python 中没有像 C++ 表示的字符类型(char),所有的字符或串都被统一为 str 对象。如单个字符 c 的类型也为 str。

str 类型会被经常使用,先列举 5 个被高频使用的方法。

strip 用于去除字符串前后的空格:

In [1]: '  I love python\t\n  '.strip()
Out[1]: 'I love python'
replace 用于字符串的替换:

In [2]: 'i love python'.replace(' ','_')
Out[2]: 'i_love_python'
join 用于合并字符串:

In [3]: '_'.join(['book', 'store','count'])
Out[3]: 'book_store_count'
title 用于单词的首字符大写:

In [4]: 'i love python'.title()
Out[4]: 'I Love Python'
find 用于返回匹配字符串的起始位置索引:

In [5]: 'i love python'.find('python')
Out[5]: 7
举个应用字符串的案例,判断 str1 是否由 str2 旋转而来。

字符串 stringbook 旋转后得到 bookstring,写一段代码验证 str1 是否为 str2 旋转得到。

转化为判断:str1 是否为 str2+str2 的子串。

下面函数原型中,注明了每个参数的类型、返回值的类型,增强代码的可读性和可维护性。

def is_rotation(s1: str, s2: str) -> bool:
    if s1 is None or s2 is None:
        return False
    if len(s1) != len(s2):
        return False

    def is_substring(s1: str, s2: str) -> bool:
        return s1 in s2
    return is_substring(s1, s2 + s2)
测试函数 is_rotation:

r = is_rotation('stringbook', 'bookstring')
print(r)  # True

r = is_rotation('greatman', 'maneatgr')
print(r)  # False
代码执行过程,动画演示:



字符串的匹配操作除了使用 str 封装的方法外,Python 的 re 正则模块功能更加强大,写法更为简便,广泛适用于爬虫、数据分析等。

下面这个案例实现:密码安全检查,使用正则表达式非常容易实现。

密码安全要求:

要求密码为 6 到 20 位;
密码只包含英文字母和数字。
import re
pat = re.compile(r'\w{6,20}') # 这是错误的,因为 \w 通配符匹配的是字母,数字和下划线,题目要求不能含有下划线
# 使用最稳的方法:\da-zA-Z 满足“密码只包含英文字母和数字”
# \d匹配数字 0-9
# a-z 匹配所有小写字符;A-Z 匹配所有大写字符
pat = re.compile(r'[\da-zA-Z]{6,20}')
选用最保险的 fullmatch 方法,查看是否整个字符串都匹配。

以下测试例子都返回 None,原因都在解释里。

pat.fullmatch('qaz12') # 返回 None,长度小于 6
pat.fullmatch('qaz12wsxedcrfvtgb67890942234343434') # None 长度大于 22
pat.fullmatch('qaz_231') # None 含有下划线
下面这个字符串 n0passw0Rd 完全符合:

In [20]: pat.fullmatch('n0passw0Rd')
Out[20]: <re.Match object; span=(0, 10), match='n0passw0Rd'>
自定义类型

Python 使用关键字 class 定制自己的类,self 表示类实例对象本身。

一个自定义类内包括属性、方法,其中有些方法是自带的。

类(对象):

class Dog(object):
    pass
以上定义一个 Dog 对象,它继承于根类 object,pass 表示没有自定义任何属性和方法。

下面创建一个 Dog 类型的实例:

wangwang = Dog()
Dog 类现在没有定义任何方法,但是刚才说了,它会有自带的方法,使用 __dir__() 查看这些自带方法:

In [26]: wangwang.__dir__()
Out[26]:
['__module__',
'__dict__',
'__weakref__',
'__doc__',
'__repr__',
'__hash__',
'__str__',
'__getattribute__',
'__setattr__',
'__delattr__',
'__lt__',
'__le__',
'__eq__',
'__ne__',
'__gt__',
'__ge__',
'__init__',
'__new__',
'__reduce_ex__',
'__reduce__',
'__subclasshook__',
'__init_subclass__',
'__format__',
'__sizeof__',
'__dir__',
'__class__']
有些地方称以上方法为魔法方法,它们与创建类时自定义个性化行为有关。比如:

__init__ 方法能定义一个带参数的类;
__new__ 方法自定义实例化类的行为;
__getattribute__ 方法自定义读取属性的行为;
__setattr__ 自定义赋值与修改属性时的行为。
类的属性:

def __init__(self, name, dtype):
     self.name = name
     self.dtype = dtype
通过 __init__,定义 Dog 对象的两个属性:name、dtype。

类的实例:

wangwang = Dog('wangwang','cute_type')
wangwang 是 Dog 类的实例。

类的方法:

def shout(self):
    print('I\'m %s, type: %s' % (self.name, self.dtype))
注意:

自定义方法的第一个参数必须是 self,它指向实例本身,如 Dog 类型的实例 dog;
引用属性时,必须前面添加 self,比如 self.name 等。
总结以上代码:

In [40]: class Dog(object):
    ...:     def __init__(self,name,dtype):
    ...:         self.name=name
    ...:         self.dtype=dtype
    ...:     def shout(self):
    ...:         print('I\'m %s, type: %s' % (self.name, self.dtype))

In [41]: wangwang = Dog('wangwang','cute_type')

In [42]: wangwang.name
Out[42]: 'wangwang'

In [43]: wangwang.dtype
Out[43]: 'cute_type'

In [44]: wangwang.shout()
I'm wangwang, type: cute_type
看到创建的两个属性和一个方法都被暴露在外面,可被 wangwang 调用。这样的话,这些属性就会被任意修改:

In [49]: wangwang.name='wrong_name'

In [50]: wangwang.name
Out[50]: 'wrong_name'
如果想避免属性 name 被修改,可以将它变为私有变量。改动方法:属性前加 2 个 _ 后,变为私有属性。如:

In [51]: class Dog(object):
    ...:     def __init__(self,name,dtype):
    ...:         self.__name=name
    ...:         self.__dtype=dtype
    ...:     def shout(self):
    ...:         print('I\'m %s, type: %s' % (self.name, self.dtype))
同理,方法前加 2 个 _ 后,方法变为“私有方法”,只能在 Dog 类内被共享使用。

但是这样改动后,属性 name 不能被访问了,也就无法得知 wangwang 的名字叫啥。不过,这个问题有一种简单的解决方法,直接新定义一个方法就行:

def get_name(self):
    return self.__name
综合代码:

In [52]: class Dog(object):
    ...:     def __init__(self,name,dtype):
    ...:         self.__name=name
    ...:         self.__dtype=dtype
    ...:     def shout(self):
    ...:         print('I\'m %s, type: %s' % (self.name, self.dtype))
    ...:     def get_name(self):
    ...:         return self.__name
    ...:

In [53]: wangwang = Dog('wangwang','cute_type')

In [54]: wangwang.get_name()
Out[54]: 'wangwang'
但是,通过此机制,改变属性的可读性或可写性,怎么看都不太优雅!因为无形中增加一些冗余的方法,如 get_name。

下面,通过另一个例子,解释如何更优雅地改变某个属性为只读或只写。

自定义一个最精简的 Book 类,它继承于系统的根类 object:

class Book(object):
    def __init__(self,name,sale):
        self.__name = name
        self.__sale = sale
使用 Python 自带的 property 类,就会优雅地将 name 变为只读的。

    @property
    def name(self):
        return self.__name
使用 @property 装饰后 name 变为属性,意味着 .name 就会返回这本书的名字,而不是通过 .name() 这种函数调用的方法。这样变为真正的属性后,可读性更好。

In [101]: class Book(object):
     ...:     def __init__(self,name,sale):
     ...:         self.__name = name
     ...:         self.__sale = sale
     ...:     @property
     ...:     def name(self):
     ...:         return self.__name

In [102]: a_book = Book('magic_book',100000)

In [103]: a_book.name
Out[103]: 'magic_book'
property 是 Python 自带的类,前三个参数都是函数类型。更加详细的讨论放在后面讨论装饰器时再展开。

In [104]: help(property)
Help on class property in module builtins:

class property(object)
|  property(fget=None, fset=None, fdel=None, doc=None)
如果使 name 既可读又可写,就再增加一个装饰器 @name.setter。

In [105]: class Book(object):
     ...:     def __init__(self,name,sale):
     ...:         self.__name = name
     ...:         self.__sale = sale
     ...:     @property
     ...:     def name(self):
     ...:         return self.__name
     ...:     @name.setter
     ...:     def name(self,new_name):
     ...:         self.__name = new_name

In [106]: a_book = Book('magic_book',100000)

In [107]: a_book.name = 'magic_book_2.0'

In [108]: a_book.name
Out[108]: 'magic_book_2.0'
注意这种装饰器写法:name.setter,name 已经被包装为 property 实例,调用实例上的 setter 函数再包装 name 后就会可写。对于 Python 入门者,可以暂时不用太纠结这部分理论,使用 Python 一段时间后,再回过头来自然就会理解。

小结

今天学习 Python 的四大基本数据类型。数值型 int、float 等;容器型 list、dict、tuple、set 等;字符型 str 与正则表达式介绍;自定义类的基本语法规则,class、属性和方法等。
专注大健康产业。寻医问药,专业资深医学顾问微信hebzhyrlm解决您与家人所有健康问题。出具最全面的健康调理解决方案。
路由世界,交换未来  有才而性缓定属大才,有智而气和斯为大智
用理想和信念来支撑自己的精神;用平和和宽容来看待周围的人事;
用知识和技能来改善自己的生活;用理性和判断来避免人生的危机;
用主动和关怀来赢得别人的友爱;用激情和毅力来实现自己的梦想;
遇事不强求,凡事有定数

TOP

返回列表