python基础数据类型
0x-wen

1.基础数据类型

1.1 变量赋值和引用

  • 每定义一个新变量时,就会在内存中开辟一块空间用于数据存储。不同的变量,内存地址是不同的。
  • 使用 id() 获取内存地址,使用 is 判断变量内存地址是否相同。
  1. 把一个变量的内存地址同时关联到另一个变量上,称为引用。两个变量对应同一块内存地址

    1
    2
    3
    4
    a = 100
    b = a
    assert id(a) == id(b)
    assert a is b
  2. 不可变类型变量和可变类型变量在引用时的区别

    常用int、字符串都是不可变类型, 字典、集合、列表都属于可变类型的变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    # b引用a所指向的数据
    a = 100
    b = a
    assert id(a) == id(b)
    assert a is b

    a = 100
    b = a
    a = 200
    print(a, b) # 200 100
    assert a is not b
    assert id(a) != id(b)

    a1 = "str"
    b1 = a1
    a1 = "string"
    print(a1, b1) # string str

    a = [1, 2, 3, 4]
    b = a
    a[0] = 100
    print(a[0], b[0]) # 100, 100
    assert a is b
    assert id(a) == id(b)

1.2 小整数池和字符串驻留

Python中存在一个小整数池,范围通常在-5到256之间。在这个范围内的整数会被提前创建并缓存,以便节省内存。

字符串驻留范围:英文字母、数字、下划线

以下代码在Ipthon中执行,使用Vscode和PythonCharm执行结果可能不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 在ipython中执行
a = 100
b = 100
print(a is b) # True

a = 500
b = 500
print(id(a) == id(b)) # False

a, b = 500, 500
print(a is b) # True 一行定义两个相同值的变量,解释器会优化,a、b是同一内存地址

a = "abc"
b = "abc"
print(a is b) # True

a = "abc!"
b = "abc!"
print(a is b) # False

1.3 数据类型

数值: int float bool complex(复数)
序列: str list tuple
散列: set dict

  • 常见基础面试题

    • 列表去重方案

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      # 方案一  set() 利用集合去重的特性
      original_list = [1, 2, 3, 4, 3, 2, 1]
      deduplicated_list = list(set(original_list))
      print(deduplicated_list) # 输出: [1, 2, 3, 4]

      # 方案二 使用enumerate 找到列表下标判断元素是否存在
      deduplicated_list = [x for i, x in enumerate(original_list) if x not in original_list[:i]]
      print(deduplicated_list) # 输出: [1, 2, 3, 4]

      # 方案三 使用 dict.fromkeys():利用字典的键的唯一性
      deduplicated_list = list(dict.fromkeys(original_list))
      print(deduplicated_list) # 输出: [1, 2, 3, 4]

      # 方案四 使用 Counter 是 collections 模块提供的一个计数器对象,可以用来统计元素出现的次数
      deduplicated_list = list(Counter(original_list)) # Counter({1: 2, 2: 2, 3: 2, 4: 1})
      print(deduplicated_list) # 输出: [1, 2, 3, 4]
    • 字符串反转

      1
      2
      3
      print("第一种方式:", "".join(reversed(data)))

      print("第二种方式:", data[::-1])
  • 推导式

    list推导式

    1
    result = ["data{}".format(i) for i in range(0, 100) if i % 2 == 0]

    dict 推导式

    1
    print({f"data{(i + 1)}": i + 1 for i in range(3)})  # {'data1': 1, 'data2': 2, 'data3': 3}

    set 推导式

    1
    { expression for item in Sequence if conditional }

    tuple 推导式 (生成器表达式)

    1
    2
    a = (x for x in range(1,10))  # <generator object <genexpr> at 0x7faf6ee20a50>  生成器对象
    tuple(a) # 使用 tuple() 函数,可以直接将生成器对象转换成元组

1.4 collections

这个模块实现了一些专门化的容器,提供了对 Python 的通用内建容器 dict、list、set 和 tuple 的补充。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from collections import namedtuple, deque, ChainMap

# namedtuple 命名元祖
Student = namedtuple('Students', ["name", "age", 'index'])
tu1 = Student(name="张三", age=18, index=1)
print(tu1.name) # 张三
print(isinstance(tu1, tuple)) # True 也是元祖类型数据
print(type(tu1)) # tu1数据类型 <class '__main__.Students'> Student对象
# deque 双端队列
d = deque('ghi')
d.extendleft('123', )
d.appendleft('4')
print(d) # deque(['4', '1', '2', '3', 'g', 'h', 'i'])
# ChainMap 将多个字典或者其他映射组合在一起,创建一个单独的可更新的视图
dict1 = {'music': 'bach', 'art': 'rembrandt'}
dict2 = {'art': 'van gogh', 'opera': 'carmen'}
dict3 = {'opera': 'dict3', 'test': 'dict3'}
c = ChainMap(dict1, dict2, dict3)
# 获取map中的key和其传入参数有关系, 迭代顺序是通过从后往前扫描
print(c.get('art'), c.get('opera')) # rembrandt, carmen
# 如果要实现dict.update功能,可以使用update()
c.update(dict3)
print(c.get('art'), c.get('opera')) # rembrandt, dict3

1.5 iterable

可迭代对象: 可被 for 遍历都是可迭代对象

  1. 实现了 iter 方法,并且该方法返回一个迭代器对象。
  2. 实现了 getitem 方法,并且可以通过索引访问元素。

class collections.abc.Iterable

提供了 __iter__() 方法的抽象基类。

使用 isinstance(obj, Iterable) 可以检测一个类是否已经注册到了 Iterable 或者实现了 __iter__() 函数,但是无法检测这个类是否能够使用 __getitem__() 方法进行迭代。检测一个对象是否是 iterable 的唯一可信赖的方法是调用 iter(obj)

示例1: 实现 __iter__ 但是返回一个list 非迭代器对象

1
2
3
4
5
6
7
8
9
10
11
12
class Iterable1:
def __init__(self):
self.data = [1, 2, 3, 4]

def __iter__(self):
# 返回的是一个列表,而不是一个迭代器对象
return self.data


obj1 = Iterable1()
assert isinstance(obj1, Iterable) # True
assert iter(obj1) # iter() returned non-iterator of type 'list'

示例2: 实现 __iter__ 返回一个迭代器对象

1
2
3
4
5
6
7
8
9
10
class Iterable2:
def __init__(self):
self.data = [1, 2, 3, 4]

def __iter__(self):
# 返回的是一个迭代器对象
return iter(self.data)

obj2 = Iterable2() # obj2 是可以被for遍历的对象
print(iter(obj2)) # <list_iterator object at 0x1043bb6d0>

示例3: 实现 __getitem__ 访问元素

1
2
3
4
5
6
7
8
9
10
class Iterable3:
def __init__(self):
self.data = [1, 2, 3, 4, 5]

def __getitem__(self, index):
# 通过索引访问元素,实现迭代行为
return self.data[index]

my_iterable = Iterable3()
print(iter(my_iterable))

1.6 iterator

迭代器:必须要同时拥有 __iter____next__ 方法才是迭代器

迭代器调用 __next__ 方法会调用迭代器中的下一个值

示例1:通过 iter(iterable) 得到迭代器

1
2
3
4
my_iterator = iter(["1", "2", "3"])
# hasattr 判断某个对象是否包含某个属性信息
print(hasattr(my_iterator, "__iter__")) # True
print(hasattr(my_iterator, "__next__")) # True

示例2:实现一个迭代器,必须要实现 __next____iter__ 方法

示例中并没有手动实现 __iter__ 会使用父类的 __iter__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from typing import Iterator


class Students(Iterator):
def __init__(self):
self.students = ["张三", "李四", "王五"]
self.index = 0

def __next__(self):
if self.index >= len(self.students):
raise StopIteration
self.index += 1
return self.students[self.index - 1]


print(isinstance(Students(), Iterator)) # True
print(my_iterator.__iter__()) # <__main__.Students object at 0x1009a7950>
for item in Students():
print(item)

示例3:实现一个迭代器,自己实现 __init__ 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Students2(Iterator):
def __init__(self):
self.students = ["1", "2"]
self.index = 0

def __iter__(self):
return iter(self.students)

def __next__(self):
if self.index >= len(self.students):
raise StopIteration
self.index += 1
return self.students[self.index - 1]


my_iterator2 = Students2()
print(my_iterator2.__iter__()) # <list_iterator object at 0x104f37fd0>
print(isinstance(my_iterator2, Iterator)) # True
print(next(my_iterator2)) # 1
print(next(my_iterator2)) # 2
print(next(my_iterator2)) # raise StopIteration

示例4:实现一个 range 迭代器

  1. range 方法的签名 start、stop 两个参数
  2. __iter__ 方法要求返回值必须是一个”迭代器“ (或者返回值必须要有 __next__ 方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Next:
def __init__(self, stop, start=-1):
self.start = start
self.stop = stop

def __next__(self):
if self.start >= self.stop - 1:
raise StopIteration
self.start += 1
return self.start


class MyRange:
def __init__(self, stop):
self.stop = stop

def __iter__(self):
return Next(self.stop)


my_range = MyRange(5) # <__main__.MyRange object at 0x1045029d0>
# False 断言它不是一个迭代器,但是它可以被for遍历,所以__iter__返回值有__next__方法也可以
print(isinstance(my_range, Iterator))
for item in my_range:
print(item) # 也可以通过 for 遍历

示例5: 基于MyRange使用while实现for

for 会自动调用 __iter__ , __next__ 方法,但是while不会,需要手动调用

1
2
3
4
5
6
7
8
9
10
11
# 基于MyRange使用while实现for
def my_while():
start, stop = 0, 5
my_range = MyRange(stop)
numbers = my_range.__iter__() # 手动调用__iter__方法
while start < stop:
print(numbers.__next__())
start += 1


my_while()

1.7 generator

生成器(高效):生成器是特殊的迭代器,迭代器是特殊的可迭代对象,那么生成器必定是可迭代对象

使用yield关键字返回一个生成器对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from typing import Iterable, Iterator


def g_func2():
my_list = range(3)
for i in my_list:
yield i * i


g = g_func2()

print(isinstance(g, Iterable)) # True
print(g.__iter__()) # <generator object g_func1 at 0x10271fc10>
print(next(g))
print(next(g))
print(next(g))
print(hasattr(g, "__iter__")) # True
print(hasattr(g, "__next__")) # True
print(isinstance(g, Iterator)) # True

1.8 for循环的本质

  1. 调用iter(),将numbers转化为迭代器numbers_iterator
  2. 调用next(numbers_iterator),返回出numbers的第一个元素
  3. 循环步骤2,迭代完numbers内所有数据,捕获异常
1
2
3
4
5
6
7
8
9
10
11
12
# while + iterator
numbers = [1, 2, 3, 4]
numbers_iterator = iter(numbers)
while True:
try:
print(next(numbers_iterator))
except StopIteration: # 捕捉异常终止循环
break

# for循环
for i in numbers:
print(i)

1.9 itertools

为高效循环而创建迭代器的函数

1
2
3
4
5
6
7
8
9
10
11
iterable = itertools.chain(["A", "B", "C"], ["D", "E", "F"])
for i in iterable:
print(i) # --> A B C D E F

from_iterable = itertools.chain.from_iterable(['ABC', 'DEF'])
for i in from_iterable:
print(i)

r = itertools.combinations("ABCD", 2)
for i in r:
print(i) # --> AB AC AD BC BD CD

1.10 lambda、map、zip

  • lamdba 处理简单业务逻辑

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    y: any = lambda x: x + 1
    print(y(10))

    Students = [
    {"name": "a", "age": 18},
    {"name": "c", "age": 20},
    {"name": "b", "age": 19},
    {"name": "ca", "age": 19},
    {"name": "cb", "age": 19}
    ]
    # 根据age排序,age一致时根据name排序
    print(sorted(Students, key=lambda student: (student["age"], student["name"])))
  • map(func, *iterables) –> map object

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # map 可迭代对象元素合并 返回新的map对象,按最短的对象合并
    a = [1, 2, 3]
    b = [4, 5, ]
    # map(func, *iterables)
    # func --> lambda a1, b1: (a1, b1)
    # *iterables --> a, b
    num1 = map(lambda a1, b1: (a1, b1), a, b) # <map object at 0x109efed60>
    for i in num1:
    print(i) # (1, 4), (2, 5)
  • reduce 求和

    1
    2
    from functools import reduce
    print(reduce(lambda x, y: x + y, range(1, 101))) # 5050
  • filter 过滤

    1
    print(list(filter(lambda x: x > 5, range(10))))  # <filter object at 0x106ad6c40>
  • zip

    1
    2
    3
    a = [1, 2, 3]
    b = [4, 5, 6, 7, 8]
    print(list(zip(a, b))) # [(1, 4), (2, 5), (3, 6)] # 元素个数与最短的列表一致

1.11 namespace

  • 一般有三种命名空间:

    1. 内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
    2. 全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
    3. 局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)
  • Python的作用域一共有4种【规则顺序: L –> E –> G –> gt; B】

    1. L(Local):最内层,包含局部变量,比如一个函数/方法内部。

    2. E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。

      比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。

    3. G(Global):当前脚本的最外层,比如当前模块的全局变量。

    4. B(Built-in): 包含了内建的变量/关键字等。最后被搜索

  • 闭包

    1.在一个函数内部定义了另一个函数
    2.内部函数引用了外部函数的变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    g_count = 0  # 全局作用域


    def outer():
    o_count = 1 # 闭包函数外,函数中
    print(f"Enclosing: {o_count}")

    def inner():
    i_count = 2 # 局部作用域
    print(f"Local: {i_count}")
    nonlocal o_count # 外层作用域
    o_count += 5
    print(f"Enclosing: {o_count}")

    return inner # 返回函数名称 可以被调用


    print(f"Global: {g_count}") # Global: 0
    func = outer() # Enclosing: 1
    func() # Local: 2 Enclosing: 6
    func() # Local: 2 Enclosing: 11

1.12 private_name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# import 私有变量
# 1. __name它不会被导入到导入模块的命名空间中
# 2. _name会被导入到导入模块的命名空间中
class MyClass:
def __init__(self):
self.__name = "Private Name" # 私有变量 __name
self._name = "Conventionally Private Name" # 约定上的私有变量 _name

def get_private_name(self):
return self.__name

def get_conventionally_private_name(self):
return self._name


obj = MyClass()

# 访问私有变量 __name
print(obj.get_private_name()) # 输出: Private Name
# print(obj.__name) # 错误,在类外部,无法直接访问私有变量,会引发 AttributeError 错误
print(obj._MyClass__name) # 输出: Private Name,通过名称重整方式访问私有变量

# 访问约定上的私有变量 _name
print(obj.get_conventionally_private_name()) # 输出: Conventionally Private Name
print(obj._name) # 输出: Conventionally Private Name,可以直接访问约定上的私有变量
由 Hexo 驱动 & 主题 Keep
总字数 42.8k