python学习笔记

笔记 · 2023-09-14 · 588 人浏览

函数

字符串

  • 字符串的rstrip()方法能删除字符串末尾的空白
  • splitlines()方法逐行读取
  • lstrip()删除左端空格

创建副本

python的函数在传递列表后如果要对列表进行修改操作的话,我们的原列表如果需要保留,那么作为传递得的列表需要时一个副本,这样进行修改操作不会影响到原始列表

要创建副本,一般使用切片[:],在调用时创建列表的副本:

function_name(list_name[:])

传递任意数量的实参

在python函数中我们会遇到不能确定传入多少参数到一个函数中的情况,这种情形下我们可以用*加在形参名称前来解决:

def function_name(*args)

这样我们在调用函数时,传递进去的所有参数都会被添加到一个元组当中被传递给函数,即使我们只传入一个参数也是如此

('item1')
('item1','item2','item3')

tips:通用形参名*args,也是这样收集任意数量的位置实参

使用任意数量的关键字实参

有时候会遇到预先不知道要传递给函数会是什么样的信息,这种情况下可以使用**前缀来传递函数任意数量的键值对信息:

def function_name(**kwargs)

**kwargs创建出的字典可以让函数接收任意数量的键值对,在函数中可以像访问其他字典一样访问kwargs

函数实参顺序规则

Python中不同类型的实参有着严格的顺序规则,Python函数参数的顺序应该遵循以下规则:

  1. 位置实参
  2. 带有默认值的实参
  3. *args(用于收集任意数量的位置实参)
  4. **kwargs(用于收集任意数量的关键字实参)

例如:

def my_function(a, b, c=0, d=0, *args, **kwargs):
    print(f"a: {a}")
    print(f"b: {b}")
    print(f"c: {c}")
    print(f"d: {d}")
    print(f"args: {args}")
    print(f"kwargs: {kwargs}")

# 调用函数
my_function(1, 2, 3, 4, 5, 6, 7, x=8, y=9, z=10)

该例子输出为:

a: 1
b: 2
c: 3
d: 4
args: (5, 6, 7)
kwargs: {'x': 8, 'y': 9, 'z': 10}

\_\_init\_\_()方法

__init__()方法在类创建实例时由Python自动运行,init两边都有两个下划线避免Python默认方法与普通方法发生名称冲突,如果缺少下划线在创建实例时Python不会运行这个方法。

__init__()方法定义时形参self必不可少且必须位于其他形参前,Python在创建实例时自动传入实参self,该实参是一个指向实例本身的引用,让实例能够访问类中的属性和方法。

self为前缀的变量可以在所有方法使用,可以通过类的任意实例来访问,像self.xxx这样的可以通过实例访问的变量称为属性。

示例:

class Restaurant:
    def __init__(self, restaurant_name, cuisine_type):
        self.restaurant_name = restaurant_name
        self.cuisine_type = cuisine_type

    def describe_restaurant(self):
        print(f'Restaurant Name:{self.restaurant_name}')
        print(f'Cuisine Type:{self.cuisine_type}')

    def open_restaurant(self):
        print(f'{self.restaurant_name} is opening')


restaurant = Restaurant('McDonald\'s', 'Fast food')
restaurant.describe_restaurant()
restaurant.open_restaurant()

运行结果:

Restaurant Name:McDonald's
Cuisine Type:Fast food
McDonald's is opening

Python类的数据成员

Python的所有数据成员都是"公有的"Python 中的类并没有像 C++ 或 Java 那样严格的访问控制机制(privateprotectedpublic)。然而,Python 社群有一些约定来模拟这种行为。

  1. 公有成员(Public): 默认情况下,所有成员都是公有的。

    class MyClass:
        def __init__(self):
            self.public_member = "This is public."
  2. 受保护的成员(Protected): 按照约定,一个下划线前缀的成员被视为受保护的。它仍然可以被外部访问,但这样的用法通常不推荐。

    class MyClass:
        def __init__(self):
            self._protected_member = "This is protected."
  3. 私有成员(Private): 使用双下划线前缀的成员会被视为私有成员。Python 会改写(名为 name mangling)这些成员的名称,所以直接访问它们会比较困难(但不是不可能)。

    class MyClass:
        def __init__(self):
            self.__private_member = "This is private."

    虽然这样的成员在技术上仍然可以被外部访问,但这需要使用特殊的语法:

    obj = MyClass()
    print(obj._MyClass__private_member)  # 输出 "This is private."
  4. 约定的公有和私有成员: 除了这些规则,Python 还有一些特殊的属性和方法名称约定。例如,以双下划线开始和结束的方法(__init__, __call__, 等)是特殊方法,通常不应被视为私有成员。

总体而言,Python 更侧重于“成年人的同意”("we're all consenting adults here")这一理念,意味着它依赖于程序员遵循这些不强制执行的命名约定,而不是强制限制成员的访问。这给予了更多的灵活性,但也要求程序员更加小心。

Python类列表默认值以及会出现的问题

使用可变的默认参数是Python中的一个常见的陷阱,下面是一个简单的例子:

class MyClass:
    def __init__(self):
        self.my_list = []  # 初始化一个空列表作为属性

# 创建类的实例
obj = MyClass()

# 输出实例的my_list属性,应该是一个空列表
print(obj.my_list)

# 向列表中添加元素
obj.my_list.append(1)
obj.my_list.append(2)
obj.my_list.append(3)

# 输出更新后的列表
print(obj.my_list)  # 输出 [1, 2, 3]

下面这个方式可以避免这个问题:

class MyClass:
    def __init__(self, initial_list=None):
        if initial_list is None:
            self.my_list = []
        else:
            self.my_list = initial_list

# 创建类的实例,并传入一个初始列表
obj = MyClass([1, 2, 3])

# 输出实例的my_list属性
print(obj.my_list)  # 输出 [1, 2, 3]

但是if...else的条件控制还是很繁琐,我们可以使用or来代替:

class MyClass:
    def __init__(self, initial_list=None):
        self.my_list = initial_list or []

在Python中,or运算符返回第一个为"真"的值。因此,如果initial_listNone(在Python中被认为是"假"的),则or运算符将返回其后面的值,即空列表[]

类的继承和组合

Python类的继承是在class后面的类名跟一个(),()内是要继承的类的名字,该类要在__init__方法中使用super().__init__调用父类的__init__方法来初始化构造父类。

一个类中可以将另一个类的实体化对象作为该类中的属性。

下面是类的继承和组合的简单示例:

class User:
    def __init__(self, first_name, last_name, sex):
        self.fname = first_name
        self.lname = last_name
        self.sex = sex
        self.login_attempts = 0

    def describe_user(self):
        print(f'User name is {self.fname} {self.lname}')
        print(f'User sex is {self.sex}')

    def greet_user(self):
        print(f'Hello! {self.fname} {self.lname}')

    def increment_login_attempts(self):
        self.login_attempts += 1

    def reset_login_attempts(self):
        self.login_attempts = 0


class Admin(User):
    def __init__(self, first_name, last_name, sex, privileges=None):
        super().__init__(first_name, last_name, sex)
        self.privileges = Privileges()


class Privileges:
    def __init__(self):
        self.privileges = ('can add post', 'can delete post', 'can ban user')

    def show_privileges(self):
        print(f'Admin\'s privileges are {self.privileges}')


admin = Admin('t', 't', 'man', ('can add post', 'can delete post', 'can ban user'))
admin.privileges.show_privileges()

运行结果:

Admin's privileges are ('can add post', 'can delete post', 'can ban user')

文件和异常

读取文件

读取文件需要pathlib模块,通过模块导入Path类,Path对象指向一个文件,可以用来核实文件是够存在,读取的文件的内容,以及将新数据写入文件。

from pathlib import Path

初始化Path对象需要将文件的相对路径或者绝对路径当做实参传入:

path = Path('text_files/filename.txt')#相对路径(linux)
path = Path('/home/data_files/text_files/filename.txt')#绝对路径(linux)

读取文件内容需要用到Path类的read_text()方法,该方法会将文件对象的全部内容作为一个字符串返回,这个字符串相比于原始文件唯一不同的地方就是末尾会多一个空行,可以使用字符串的rstrip()来消除字符串末尾的空白:

contents = path.read_text().rstrip()

逐行访问可以使用字符串的splitlines()函数,该函数返回一个列表包括文件中的所有行,可以对其遍历访问:

lines = path.read_text().splitlines()

for line in lines:
    print(line)

写入文件

可以使用Path类中的write_text()方法来进行文件写入,该方法接受单个实参,即要写入文件的字符串,没有终端输出。Python只能将字符串写入文本文件,其他格式需要使用str()来进行转换。

使用write_text()方法时如果path变量对应的路径指向的文件不存在就会创建它。

要进行多行写入时,需要创建一个字符串将多行内容传递给这一个字符串,在将此字符串传递给write_text()方法:

from pathlib import Path
contents = 'I love programming.\n'
contents += 'Ilove creating new games.\n'
contents += 'I also love working with data.\n'

path = Path('programming.txt')
path.write_text('contents')

tips:使用该方法需谨慎,如果原文件已有内容存在,write_text()方法删除其内容并将制定的内容写入。

异常

try_except代码块

python在发生异常时会创建一个异常对象,如果未对异常进行处理,程序将终止并显示一个traceback,其中包含有关异常的报告。异常是使用try_except代码块处理的,下面给出一个使用try_except代码块来处理ZeroDivisionError异常的示例

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")

如果try代码块中的代码运行起来没有问题,python将跳过except代码块,如果try代码块中的代码出现错误,python会查找与之匹配的except代码块并运行其中的代码。

else代码块

只有try代码块成功执行才需要继续执行的代码,都应该放到else代码块中:

try:
    print(5/0)
except ZeroDivisionError:
    print("You can't divide by zero!")
else:
    print('over')

静默

如果想让程序在发生异常时既不进行处理也不进行traceback,我们可以用pass来保持静默:

try:
    --snip--
except FileNotFoundError:
    pass
else:
    --snip--

except中的pass告诉python什么都不要做,虽然执行except但什么都不会发生。

Theme Jasmine by Kent Liao