欢迎大家关注我的公众号『Python3X』,扫描下方二维码( 或微信搜索:python3xxx)。即可关注。

主要分享Python类技术干货,包括不限于Python技术、Web开发、爬虫、数据分析等,当然还有不定期的工具干货分享。

Image text

目录索引:

001、Python中的可变对象与不可变对象

002、类的定义和装饰器@classmethod与@staticmethod

003、删除列表中的重复元素

004、Python中的lambda表达式

005、Python中的可变参数

006、Python中的super()用法

007、Python中的深浅拷贝

008、Python中文件的读取

--

001、Python中的可变对象与不可变对象

对象分为 可变对象不可变对象。 可通过函数 id() 查看对象的内存地址是否改变

  • 不可变对象:改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。 如:String、Tuple、Number。他们本身的值是不可以被改变,修改的时候,会复制一个新的对象,并开辟一份新的内存空间,变量再去指向新的值。
a = 1
print(id(a)) # 4543532032
a = 1 + 2
print(id(a)) # 4543532096
# 证明字符串是可变对象
  • 可变对象:意味着修改该数据对象,不会在内存中新创建另一个内存空间来存放新数据对象,如:List、Dict、Set。对其进行修改时,并不会像可变对象那样重新复制一份。而是在原有的基础上进行修改。
a = [1, 2]
print(id(a)) # 4362993544
a.append(3)
print(id(a)) # 4362993544
a[1] = 5
print(id(a)) # 4362993544
# 列表是不可变对象

002、类的定义和装饰器@classmethod与@staticmethod

参考此文:https://mp.weixin.qq.com/s/t1T_K5wbovOAIdzgKjIwhg

003、删除列表中的重复元素

1、通过set方法

a = [1, 2, 3, 1, 1, 1, 7, 9, 5]
print(list(set(a)))

2、通过fromkeys方法

a = [1, 2, 3, 1, 1, 1, 7, 9, 5]
b = {}
# fromnkeys 创建一个新的字典,已a中的元素作为字典的键
b = b.fromkeys(a)
print(b)  # {1: None, 2: None, 3: None, 7: None, 9: None, 5: None}
c = list(b.keys())
print(c)  # [1, 2, 3, 7, 9, 5]

3、逻辑判断

a = [1, 2, 3, 1, 1, 1, 7, 9, 5]
b = []

for i in a:
    if i not in b:
        b.append(i)

print(b) # [1, 2, 3, 7, 9, 5]

004、Python中的lambda表达式

lambda又称为匿名函数,可以不再去定义函数,使代码更加简洁。

def m(x):
    return x ** 2
print(m(2))
# 等同于函数m()
m1 = lambda x: x ** 2
print(m1(2))

更多用法,请参考此文:https://mp.weixin.qq.com/s/CsBpTh0cDyyOT0BZ5cUHwg

005、Python中的可变参数

可变参数的形式有

  • *args : 元组类型参数
def fun_arr(name, *args):
    print(name)
    print(args)

if __name__ == '__main__':
    fun_arr('tom')  # 输出 tom 、()
    # tom
    # (20, 'man', '篮球', 'pythoner', '17766666666')
    fun_arr('tom', 20, 'man', '篮球', 'pythoner', '17766666666')
  • **kwargs :字典类型参数
def fun_dict(**kwargs):
    for k, v in kwargs.items():
        print(k, v)
    # 也可通过get的方式取参数的值
    # my name is tom i'am 18 years old, my phone number is 17766666666
    print("my name is %s i'am %s years old, my phone number is %s"
          % (kwargs.get('name'), kwargs.get('age'), kwargs.get('mobile')))

if __name__ == '__main__':
    fun_dict(name='tom', age=18, mobile='17766666666')

可变参数允许多个任意参数,也允许不传参数。

两个可变参数同时存在是,一定要将 *参数 放在 **参数 之前!!!否则编译错误。

通常情况下,都是将可变参数写在最后面。

如果写在前面则会将后面的参数也会收集到列表中。如果将可变参数写在前面,则需要指定后面的参数名

006、Python中的super()用法

super()是Python中内置的一个函数,用于调用父类的方法。

class A():
    def eat(self):
        print('i eat food')

class B(A):
    def run(self):
        print('i can run')

b = B()
# 调用父类的eat
b.eat() # i eat food

如果一个子类继承了两个父类,并且父类中的方法名相同。那究竟会调用哪个类呢?

这个问题可以通过类的内置属性 mro 去查看调用顺序。这个方法主要是在多继承时判断方法、属性的调用路径。

class A:
   num = 1
   def method(self):
       print('A ..method')

class B:
   num = 2
   def method(self):
       print('B .. method')


class C(A, B):
   num = 3
   pass

if __name__ == '__main__':
   c = C()
   print(C.__mro__)  # (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
   print(c.num)  #  3
   c.method()  # A ..method

如上,C.mro 打印结果可以执行顺序为 C -> A - >B

因此print(c.num)。因为c中有num属性,所以会打印3.

执行c.method时 。因为c中没有method属性,会去A中查找,则输出 A..method。

007、Python中的深浅拷贝

拷贝就是对变量的复制,

浅拷贝:

拷贝对象并给其分配新的内存。但是只会拷贝父对象

# 定义一个变量a
a = ['a', 1, 'd', ['z', 'x', 'c']]
# b 浅拷贝了a
b = a.copy()

print(a) # ['a', 1, 'd', ['z', 'x', 'c']]
print(b) # ['a', 1, 'd', ['z', 'x', 'c']]
#  打印各自的内存地址,发现各不相同。证明了浅拷贝会分配一个新的内存地址
print(id(a))  # 2767408
print(id(b)) # 121575744

但是如果修改a中的元素,会怎么样呢?

# 修改a中的第一个元素为'A'
a[0] = 'A'
print(a)  # ['A', 1, 'd', ['z', 'x', 'c']]
print(b) # ['a', 1, 'd', ['z', 'x', 'c']]   发现b并没有受影响

# 修改第四个元素中的第一个元素,将z 修改成 Q
a[3][1] = 'Q'
print(a)  # ['A', 1, 'd', ['z', 'Q', 'c']]
print(b)  # ['A', 1, 'd', ['z', 'Q', 'c']]  发现b也被修改了。why?

print(id(a[3]))  # 2766208
print(id(b[3])) # 2766208

如上修改a中的第一个元素。b不会被修改。但是修改 第四个元素中的第一个元素。b却被影响了。这就是前面说的浅拷贝只会拷贝第一层数据。

第二层['z', 'Q', 'c']并没有拷贝成功。

这个时候b中的['z', 'Q', 'c'] 的内存地址和a中的['z', 'Q', 'c']内存地址是一样的。 (浅拷贝只是把这个第二层的引用拷贝过来了。)。所以a中第二层的数据发生变化,b中的数据也会跟着变化。

深拷贝:

对对象的完全拷贝,不管你有多少层,数据都不会共享。 深拷贝需要引入copy模块。import copy

还是拿上面的数据我们进行演示。

import copy

a = ['a', 1, 'd', ['z', 'x', 'c']]
b = copy.deepcopy(a)

print(a) # ['a', 1, 'd', ['z', 'x', 'c']]
print(a) # ['a', 1, 'd', ['z', 'x', 'c']]
#  变量的内存空间地址
print(id(a)) # 120265424
print(id(b)) # 15809072
print(a[-1])
# 最后一个元素(['z', 'x', 'c'])的内存地址
print(id(a[-1])) # 16171936
print(id(b[-1])) # 120265344

# 修改
a[0] = 'A'
a[3][0] ='Q'
print(a) # ['A', 1, 'd', ['Q', 'x', 'c']]
# 深拷贝中第二层数据也是独立的,不会被修改
print(b) # ['a', 1, 'd', ['z', 'x', 'c']]

008、Python中文件的读取

读取文件的方式有三种

  • read()

  • readline()

  • readlines()

以上都可以通过传入一个int类型的整数来限制读取范围。

创建文件的open函数用法:

file = open(file_name, mode='r',encoding=)

file_name:文件路径

mode:打开方式:默认'r',

encoding: 指定打开的编码

常用的打开方式:

  • r:以只读的方式打开文件,文件必须存在

  • r+ :以读写的方式打开文件,文件必须存在

  • w:以写的方式打开文件,文件若存在,则清空内容,从头写入。若不存在自动创建

  • w+:已读写的方式打开文件,文件若存在,则清空内容,从头写入。若不存在自动创建

  • a:已写的方式打开文件,如果存在,则后面追加写入,如果不存在,则创建

  • a+:已读写的方式打开文件,如果存在,则后面追加写入,如果不存在,则创建

read()

一次性读取整个文件,以字符串的形式返回,如果文件过大,会占用较大的内存空间。

try:
    # 只读模式,utf-8编码打开
    file = open('古诗', mode='r', encoding='utf-8')
    # 床前明月光,
    print(file.read(6)) # 读取前6个字符。如果不填 输入全部
except (FileNotFoundError, IOError) as e:
    print('文件异常')
finally:
    file.close()

上述代码是一种相对比较low的写法,因为每次读取文件都要关闭文件。这样做是为了操作文件占用的操作系统资源。所以一般读写文件都不这样写,而是通过with open..as..的形式去完成。

with open('text', 'r', encoding='utf-8') as file:
    """
    床前明月光,
    疑是地上霜。
    举头望明月,
    低头思故乡。
    """
    print(file.read())

readline()

按行读取文件,读取大文件的时候因为一次只读一行数据,所以不会占用较大内存。

with open('text', 'r', encoding='utf-8') as file:
    while 1:
        line = file.readline()
        if not line:
            break
        # readline() 每读取一行内容会有个换行符。
        # print(line.strip())
        print(line.replace('\n', ''))

readlines()

一次读取所有文件内容,返回列表,列表中的每个元素 代表每一行的内容。

with open('古诗', 'r', encoding='utf-8') as file:
    lines = file.readlines()
    # ['床前明月光,\n', '疑是地上霜。\n', '举头望明月,\n', '低头思故乡。']
    print(lines)
    for i in lines:
        print(i.strip())