函数

函数和模块的使用

$$
一道数学题:x1+x2+x3+x4 = 8 有多少组正整数解?
$$
等同于 将八个苹果 分成四组,每组至少一个苹果

$$C_M^N =\frac{M!}{N!(M-N)!}, \text{(M=7, N=3)} $$
python 的实现:

def fac(num):

    """求阶乘"""

    for n in range(1,num+1):

        result *=n

    return result



m = int(input("m =  "))

n = int(input("n =  "))



print(fac(m)//fac(n)//fac(m-n))

使用函数减少重复。


函数的参数

可以有默认值,也可以是可变参数

  • 设置默认值:

    def add(a =0,b = 0,c = 0):
    return a+b+c


    print(add()) #使用默认值
    print(add(1))
    print(add(1, 2))
    print(add(1, 2, 3)) #以上三种是按顺序传递参数
    print(add(c=50, a=100, b=200)) #不按照顺序传递
  • 不清楚参数个数,使用可变参数:

在参数之前 * 表示是一个可变参数

def add(*a):
total = 0
for val in a:
total+=val
return total


# 在调用add函数时可以传入0个或多个参数
print(add())
print(add(1))
print(add(1, 2))
print(add(1, 2, 3))
print(add(1, 3, 5, 7, 9))

用模块管理函数

在python 中没有函数重载,所以后面的相同命名函数 会替代前者。
所以为了避免,使用模块,python 中每个文件都代表了一个模块(module)

  • module1.py
    def foo():
    print('hello, world!')
  • module2.py
    def foo():
    print('goodbye, world!')
  • test3.py
    from module1 import foo

    # 输出hello, world!
    foo()

    from module2 import foo

    # 输出goodbye, world!
    foo()



    或者
    这里的两个as必须是不同的东西,不然前者会被后者替代。

    import module1 as m1
    import module2 as m2

    m1.foo()
    m2.foo()

注意:当我们编写的module 中除了要引用的函数,还包含其他可执行的代码时 最好将执行代码放入如下所示条件中

  • module3.py
    def foo():
    ...


    def bar():
    ...


    # __name__是Python中一个隐含的变量它代表了模块的名字
    # 只有被Python解释器直接执行的模块的名字才是__main__
    if __name__ == '__main__':
    print('call foo()')
    foo()
    print('call bar()')
    bar()

函数的嵌套

目的:创建闭包。生成一个会根据外部作用域参数动态变化的函数。就是内部函数可以访问外部函数变量。
简单理解就是:闭包可以保存状态,保存外部函数的状态

什么时候会用到闭包呢?

  • 想要一个带有特定行为的函数
def 外部函数(参数):
"""一些功能"""
def 内部函数(参数):
return 外部函数相关参数 # 这一步其实是最终想要实现的功能

return 内部函数

每当我们调用内部函数中的时候,外部的参数仍然是可以访问的。


变量的作用域

Python查找一个变量时会按照“局部作用域”、“嵌套作用域”、“全局作用域”和“内置作用域”的顺序进行搜索
权重大小相反

如果我们想要在内部函数中修改全局作用域的变量,需要使用 global
如果望函数内部的函数能够修改嵌套作用域中的变量,需要使用 nonlocal

def foo():
global a
a = 200 #局部变量
print(a) #200

if__name__ == "__main__":
a = 100 #全局变量
foo()
print(a) # 由原来的 100 变成200



正则表达式

形式

lambda <参数/变量> : 表达式
把参数返回到后面的表达式中,不需要使用return

#与函数的比较

def square(x):
return x*x

square = lambda x:x*x

print(square(5))



print((lambda x : x*x)(5))

当牵涉到把一个函数作为变量传入另一个函数中,一般把要作为变量的函数以正则表达式的形式给出

def cube(k):
return k*k
summation(5,cube)

summation (5,lambda k:k*k)

条件表达


面向对象编程

类/对象

  • class 关键字创建类,引用创建对象。
    class myclass:
    x = 5
    p1 = myclass() #创建对象
    print(p1.x)

    这里 p1 叫做对象的实例,只是一个变量名。

如何在python 中创建实例(对象)

通过键入类的名称,然后键入左括号和右括号来创建新对象:

>>> class Dog:
... pass
...
>>> Dog()
<__main__.Dog object at 0x106702d30>

>>> Dog()
<__main__.Dog object at 0x0004ccc90>

两次创建的实例位于不同的内存地址,两者不相同

  • 函数

self 属于该类的当前实例的引用“当前对象的感觉”

class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def myfunc(self):
print("Hello my name is " + self.name)

p1 = Person("John", 36)
p1.myfunc()
  • self
    用于访问属于该类的变量。可以随便命名,必须是类中任何函数的第一个参数。

  • 修改对象属性

    p1.age = 40        #修改年龄为40岁
  • 删除对象属性

    del p1.age
  • 删除对象


    del p1

  • init()函数
    所有函数都有这个函数,当类被初始化的时候使用。每次创建新对象的时候也会自动调用该函数。
    这个函数为对象属性分配值,或其他操作。

  • str()函数
    控制类的对象是字符串的时候应该返回的内容。


class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def __str__(self):
return f"{self.name},{self.age}"

p1 = Person("John", 36)

print(p1) #当使用str 函数,可以直接返回相应的内容
print(f"{p1.name},{p1.age}")

  • repr()函数
    repr 方法应返回一个“官方”的字符串表示形式,该表示形式能够准确且唯一地描述该对象
class Fraction:
pass

def __repr__(self):
# 返回一个可以用来重建这个对象的字符串
return f'Fraction({self.numerator}, {self.denominator})'

# 创建 Fraction 对象
f1 = Fraction(1, 2)
f2 = Fraction(3, 4)

# 打印对象,调用 __repr__ 方法
print(f1) # 输出: Fraction(1, 2)
print(f2) # 输出: Fraction(3, 4)

# 使用 eval 重建对象
f3 = eval(repr(f1))
print(f3) # 输出: Fraction(1, 2)

为什么要自定义 __repr__?
默认的 repr 方法仅返回对象的内存地址

str 与 repr 的区别

前者是用户友好表示 ,当使用print() 或者 str()函数的时候,会调用__str__方法
后者是官方表示,主要面向开发者,提供详细、精确的对象信息

操作符重载

操作符:加减乘除,比较操作符(用于比较两个值的大小或相等性,返回布尔值(True 或 False)),逻辑……
允许同一操作符根据上下文具有不同的含义称为操作符重载

python 标准操作符

![屏幕截图 2024-09-24 201019](C:\Users\86151\Pictures\Screenshots\屏幕截图 2024-09-24 201019.png)

为甚么要进行重新定义一些标准操作符?
比如说,我们要比较 两个对象的实例是否相等,但是因为储存的位置不一样,所以不相等。

f1 = Fraction(2,5)
f2 = Fraction(2,5)
print(f1==f2) #FALSE

所以需重新定义__eq__()

私人变量

self._name = name 在变量前加上下划线来标识私有变量
好处:确保数据的完整性。并且封装(隐藏内部表示,外部接口一致)
私有变量不能被对象外部的代码直接访问。要用get 或set 才能访问私有变量。

class person:
def __init__(self,name):
self._name = name
def get_name(self):
return self._name


继承

继承允许我们定义一个类,该类继承了另一个类的所有方法和属性。

父类是被继承的类,也称为基类。

class Fatherclass():
def __init__(self,fname,lname):
self.firstname = fname
self.lastname = lname
def printname(self):
print(self.firstname,self.lastname)

p = FatherClass("larissani","stellar")
p.printname()

子类是从另一个类继承的类,也称为派生类
父类按照正常类创建,子类创建时把父类作为参数传入。

class SonClass(FatherClass):
pass
  • init__函数
    如果想在子类添加__init__函数,一般来说,子项的__init
    函数会覆盖父类的__init__函数。为了保证继承,我们添加对父类函数的调用。
class SonClass(FatherClass):
def __init__(self,fname,lname):
FatherClass.__init__(self,fname,lname)
"""可以添加一些功能"""

  • super()函数
    在python 中也可以使用super()函数让子类继承
class Student(Person):
def __init__(self, fname, lname):
super().__init__(fname, lname)

递归

一个直接或者间接调用自己的函数。