「新」动计划 · 编程入门
两整数相加
在这段代码中,我们来解释两个关键字:
object
和self
。1. object
:
object
是 Python 中所有类的基类。换句话说,Python 中所有的类都默认继承自 object
,不论你有没有明确写出来。这样做的好处是让所有类都具备了 object
类的一些基础方法,比如 __init__
、__str__
等。这也是 Python 面向对象设计的一个基本原则——万物皆对象。在这段代码中,
class Solution(object):
的意思是 Solution
类是继承自 object
类的。虽然 Python 3 中写 class Solution:
和 class Solution(object):
是一样的,但在 Python 2 中,只有写成 class Solution(object):
才能用到 Python 的新式类机制(新式类引入了一些更强大的功能)。所以,这里的
object
是告诉 Python,Solution
类是基于 object
类创建的。2. self
:
self
是 Python 类方法中的一个“传统”参数。它代表类的实例本身,或者说是对象自身。每当你在类中定义一个方法时,Python 会默认把调用该方法的对象作为第一个参数传递给方法,这就是 self
。比如你在代码中定义了一个
sum
方法,传入 num1
和 num2
。当你用一个 Solution
类的实例去调用这个方法时,Python 会自动把这个实例作为 self
传递进去。所以即使你不显式传入 self
,Python 也会在幕后帮你传递。举个例子:
s = Solution() s.sum(1, 2)
在调用
s.sum(1, 2)
的时候,Python 实际上相当于执行了 Solution.sum(s, 1, 2)
,其中 s
就是 self
,指代 Solution
类的实例。为什么 Python 要把调用方法的对象作为第一个参数传递给方法呢?为什么一定要有 self
? 这个问题其实可以从 Python 的设计哲学和面向对象编程(OOP)的角度来理解。
1. 明确的 "自我":self
让代码更清晰
Python 是一门非常强调“清晰”、“明确”的语言,它有一个哲学——“明确优于隐式”(Explicit is better than implicit)。通过显式地把
self
作为方法的第一个参数,我们能够非常清楚地知道这个方法是与某个对象绑定的,也就是属于某个实例的,而不是一个普通的函数。举个例子,如果没有
self
,方法内部使用对象的属性时,可能会让代码变得不够明确:def sum(num1, num2): # 假设内部用了实例的属性,代码不容易理解 return num1 + self.num2
在 Python 里,每次定义类方法时都显式地传入
self
,这样一眼就能看出这个方法依赖于实例对象,不需要靠猜。2. 灵活控制:self
让你更自由
通过
self
,我们可以随时访问当前实例的属性和方法,这带来了更大的灵活性。你可以定义一个类,类中的方法可以动态地操作实例自身的数据。self
让我们在方法中能够直接操作这个对象的状态,也就是能随时修改、访问和更新对象的属性。比如:
class Person: def __init__(self, name): self.name = name def greet(self): print(f"Hello, my name is {self.name}")
每个
Person
实例可以有不同的 name
属性,self
让方法能够随时访问到这个 name
,而且不同的实例使用这个方法时,输出的内容也会不同。3. 这是 OOP 的标准做法
在面向对象编程中,类的实例方法默认就是作用于当前对象本身,这样的设计使得对象能够拥有自己的状态(属性)和行为(方法)。Python 通过
self
实现了这一机制,而许多其他编程语言(如 C++、Java)则采用了类似的机制,只是它们的 self
是隐式的,由编译器自动处理,而不是像 Python 这样显式写出来。举个对比的例子:
在 Java 里,你可能会看到类似的代码:
class Person { String name; void greet() { System.out.println("Hello, my name is " + this.name); } }
在这里,
this
和 Python 的 self
是类似的,但在 Java 中,它是可以省略的,而 Python 则要求显式写出 self
,因为这符合 Python 的哲学:明确优于隐式。4. 避免混淆
如果没有
self
,Python 就得靠某种“隐式”机制来知道哪个对象在调用某个方法。这样可能会引起混乱,特别是如果你不小心定义了两个相同名字的方法或者变量,而没有明确指明哪个是实例的属性、哪个是局部变量的话,调试起来会非常困难。所以,
self
就是为了解决这种混乱问题而设计的,通过 self
,你可以明确区分“对象的属性”和“方法内的局部变量”,这样代码阅读起来更直观,调试起来也更容易。class Person: def __init__(self, name, age): self.name = name # 这是对象的属性 self.age = age # 这是对象的属性 def celebrate_birthday(self): age = 18 # 这是方法内的局部变量,仅在方法内部生效 self.age += 1 # 通过 self 访问对象的属性,并修改它 print(f"{self.name} is now {self.age} years old!")
python的代码特点,包括
object
类、函数的参数self
温度转换
python简直太方便了
python直接列表返回,一看就懂
python变量名通常使用小写字母
解释下c,与python的区别
局部变量只在函数执行期间有效,此为栈内存
堆内存在函数结束后依然有效,直到手动
free()
在代码中,
result
是一个指向 double
类型的指针,指向了一块通过 malloc
分配的堆内存。由于这块内存是动态分配的,它不会随着函数结束而被自动回收。指针 result
保存了这块堆内存的地址,因此即使函数结束,调用者依然可以通过这个指针访问到有效的数据,直到明确调用 free()
来释放内存
通过 *returnSize = 2;
,调用者可以知道返回的数组有 2 个元素。换句话说,returnSize
告诉了调用者,result
数组中存储了 2 个温度值(开尔文和华氏温度)。在实际使用中,调用者就可以通过这个信息去正确地遍历或处理返回的数组数据。
c语言返回数组的大小还要我自己设置啊,它不能自己得知吗?没我想的智能啊
你提出的这个问题非常有趣!在 C 语言中,确实不像 Python 等高级语言那样智能,需要开发者手动管理很多事情,包括内存分配和数组的大小。这也是 C 语言的特性之一,它接近底层,给了开发者对内存和数据的精确控制,但同时也带来了更多的管理工作。
为什么 C 语言不能自动知道数组的大小?
C 语言中,数组在函数间传递时,只能传递数组的首地址(也就是指针),它不会携带有关数组大小的信息。也就是说,数组本身并不包含长度的元数据。当你传递一个数组或动态分配的内存时,函数只知道这是某块内存的起始位置,而不知道这块内存有多大。
在 C 语言中,指针和数组有以下特点:
- 数组名在函数调用中退化为指针,指向数组的第一个元素。
- 指针本身只是一个地址,并不包含指向的数据有多大。
- 动态分配的内存(如使用
malloc
分配的内存)不会自动附带大小信息,函数必须通过其他方式知道内存块的大小。
C 语言的设计理念
C 语言是一种底层的编程语言,它更关注性能和内存管理,而不是自动化处理。例如,当你使用
malloc
动态分配内存时,C 语言只会返回一块内存的首地址,内存管理完全由程序员控制。相比之下,像 Python、Java 这样的高级语言,会自动管理内存,并且它们的数组或列表自带长度信息。在 C 语言中,你要明确告诉程序内存有多大:
- 当你动态分配内存时,你需要自己指定需要多少字节的内存。
- 当函数需要返回一个数组或处理一个数组时,调用者通常需要提供数组的大小,或者通过其他方式告诉函数数组有多少元素。
高级语言与 C 语言的差异
在 Python 这样的语言中,列表对象会自动存储它们的大小,因此当你传递一个列表时,Python 知道列表有多少元素:
my_list = [1, 2, 3] len(my_list) # Python 会自动知道列表的大小
但在 C 语言中,数组传递时仅仅传递了首地址,没有内置的方法可以知道数组的长度。所以必须通过额外的机制告诉函数数组的大小。
例如,在 C 语言中常见的做法是:
- 通过额外的参数传递数组的大小。
- 返回时通过指针传递数组的大小(像你代码中的
returnSize
)。
- 使用结构体打包数组和它的大小一起传递。
你提到的智能与手动设置大小的区别
- 高级语言(如 Python):列表或数组本身会自动记录长度信息。当你传递列表或数组时,语言的运行时系统会跟踪它的长度,因此你不需要显式地管理数组的大小。
- C 语言:C 语言没有自动的内存管理或长度跟踪机制。当你传递数组或动态分配的内存时,程序只知道数组的起始地址,不知道它的大小。这是因为 C 语言设计初衷是提供高效、低开销的内存操作,而不是在运行时增加额外的元数据管理。
举个例子:
假设我们需要返回一个数组,并且告诉调用者数组的大小,常见的做法是通过
returnSize
参数返回数组大小:#include <stdlib.h> double* convertTemperature(double celsius, int* returnSize) { double* result = (double*)malloc(2 * sizeof(double)); // 分配2个double大小的数组 result[0] = celsius + 273.15; // 开尔文 result[1] = celsius * 1.8 + 32; // 华氏 *returnSize = 2; // 告诉调用者数组的大小是2 return result; // 返回数组的地址 }
如果没有 returnSize
,会发生什么?
如果你不通过
returnSize
告诉调用者数组的大小,调用者就不知道返回的数组里有多少个元素。在这种情况下,调用者可能错误地遍历数组,导致越界访问,产生不可预期的行为或程序崩溃。总结:
- C 语言设计:C 语言没有内置机制记录数组大小,因为它主要面向高性能和内存控制,这也导致你需要手动管理数组的大小。
- 为什么不能自动得知数组大小:因为数组名传递的是指针,指针仅仅指向数组的首地址,没有长度信息。因此,程序员必须显式传递数组的大小。
- 如何处理:通过额外的参数(如
returnSize
)或使用结构体来打包数组和长度信息,是 C 语言中常见的做法。
虽然这种手动管理看似麻烦,但它让你对内存有更多的控制,同时减少了高级语言中的运行时开销(如自动内存管理、垃圾回收等)。这也是为什么 C 语言被广泛用于操作系统、嵌入式系统等性能要求极高的场景。
对比python和c的一点区别
c的内存管理逻辑
python超方便的列表
最小偶倍数
求最小偶倍数的解法
以后用python3、c两种语言解题
python3类型注解
判断根节点是否等于子节点之和
二叉树的定义
树的解引用操作
检查输入 的思想
数组异或操作
异或?
位运算,相同为0,不同为1。
运用:交换两个数、找出出现过单数次数的数、加密解密
在 Python 中,
range()
函数可以根据传入的参数生成一个从起始值到结束值(不包括结束值)的整数序列。
对异或的学习
两种语言循环的写法
循环+增量赋值运算符
好数对的数目
range(stop)
:生成从0
到stop-1
的整数序列。
range(start, stop)
:生成从start
到stop-1
的整数序列。(这题很适合:)
range(start, stop, step)
:生成从start
开始,每次步长为step
,直到stop-1
的整数序列。
range()
通常用于循环控制,特别是在for
循环中。
(我又找回考试的感觉了🤣)
双重循环及其中一个技巧:j从i+1开始
统计好三元组
python用and
abs()
是一个内置函数,专门用于计算一个数的绝对值。跟上一道题很像
abs()
的运用寻找用户推荐人
考虑NULL
OR
IS NULL
NOT IN
MySQL基本查询语句
两种解法,一种正向且带有对NULL的考虑,一种是补集法
中途写作业去了,用的c++。现在决定把c替换成c++,并且只用c++
并且简单的题目不附上
**258
%=10
/=10