第七章 面向对象
7.1.1 基本概念
- 类 : 具有相同方法和属性的一类事物
- 对象、实例 : 一个拥有具体属性值和动作的具体个体,是类的一个实例
- 实例化 :从一个类得到一个具体对象的过程
7.1.2 基本格式
| |
| class 类名: |
| def 方法名(self,name): |
| print(name) |
| return 123 |
| def 方法名(self,name): |
| print(name) |
| return 123 |
| def 方法名(self,name): |
| print(name) |
| return 123 |
| |
| |
| |
| obj = 类名() |
| |
| result = obj.方法名('alex') |
| print(result) |
7.1.3 应用场景
- 应用场景:遇到很多函数,需要给函数进行归类和划分
- 三个词:类、对象、方法
- 补充:
- 类和对象是什么关系?
- self是什么?(obj.方法时会把obj传给self)
- self就是一个形式参数,对象调用方法时,python内部会将该对象传给这个参数
- 类内部代码会从上到下依次执行,类变量立即执行,方法需调用内部代码才会执行
- 类与类之间可以嵌套,执行遵循上条
- 类/方法/对象 都可以当做变量嵌套到其他类型中
- 什么时候用面向对象?
- 函数(业务功能)比较多,可以使用面向对象来进行归类
- 想要做数据封装(创建字典存储数据时,面向对象)
- 游戏示例:创建一些角色并且根据角色需要再创建人物
7.2 面向对象的三大特性
7.2.1 封装
对象(obj)的作用:存储一些值,方便以后使用
| class File: |
| def read(self): |
| with open(self.name,mode='r',encoding='ut-8')as f: |
| data =f.read() |
| return data |
| |
| def write(self,content): |
| with open(self.name,mode='a',encoding='ut-8')as f: |
| f.write(content) |
| |
| obj1=File() |
| obj1.name ="data.txt" |
| obj1.read() |
| |
| obj2 = File() |
| obj2.name ='info.txt' |
| obj2.write('alex') |
封装的作用:将数据封装到对象,方便使用 更专业的写法
| class Person: |
| def __init__(self,n,a,g): |
| self.name = n |
| self.age = a |
| self.gender = g |
| def show(self): |
| tmp ='我是%s,年龄%s,性别%s' %(self.name,self.age,self.gender) |
| print(tmp) |
| |
| |
| |
| p1 =Person('zz',19,'男') |
| p1.show() |
| |
| p2 =Person('zy',22,'男') |
| p2.show() |
| |
| |
| 我是zy,年龄22,性别男 |
总结:如果写代码时,函数比较多比较乱。
- 可以将函数/方法归类并放到同一个类中
- 函数如果有一个反复使用的公共值,则可以放到对象中
7.2.1.1 你理解的封装?
(2个,一个是封装一些方法到一个类,另一个是封装一些数据,等之后方便调用)
| |
| |
| USER_LIST = [] |
| while True: |
| user = input('请输入用户名:') |
| pwd = input('请输入密码:') |
| email = input('请输入邮箱:') |
| temp = {'username':user,'password':pwd,'email':email} |
| USER_LIST.append(temp) |
| for item in USER_LIST: |
| temp = "我的名字:%s,密码:%s,邮箱%s" %(item['username'],item['password'],item['email'],) |
| print(temp) |
| |
| |
| class Person: |
| def __init__(self,user,pwd,email): |
| self.username = user |
| self.password = pwd |
| self.email = email |
| |
| USER_LIST = [对象(用户/密码/邮箱),对象(用户/密码/邮箱),对象(用户/密码/邮箱)] |
| while True: |
| user = input('请输入用户名:') |
| pwd = input('请输入密码:') |
| email = input('请输入邮箱:') |
| p = Person(user,pwd,email) |
| USER_LIST.append(p) |
| for item in USER_LIST: |
| temp = "我的名字:%s,密码:%s,邮箱%s" %(item.username,item.password,item.email,) |
| print(temp) |
| |
| |
| class Person: |
| def __init__(self,user,pwd,email): |
| self.username = user |
| self.password = pwd |
| self.email = email |
| def info(self): |
| return "我的名字:%s,密码:%s,邮箱%s" %(item.username,item.password,item.email,) |
| |
| USER_LIST = [对象(用户/密码/邮箱),对象(用户/密码/邮箱),对象(用户/密码/邮箱)] |
| while True: |
| user = input('请输入用户名:') |
| pwd = input('请输入密码:') |
| email = input('请输入邮箱:') |
| p = Person(user,pwd,email) |
| USER_LIST.append(p) |
| for item in USER_LIST: |
| msg = item.info() |
| print(msg) |
7.22 继承
所有的查找名字(调用方法和属性)都是先找自己的,自己没有找父类
| class F3(object): |
| def f1(self): |
| ret = super().f1() |
| print(ret) |
| return 123 |
| |
| class F2(object): |
| def f1(self): |
| print('123') |
| |
| class F1(F3, F2): |
| pass |
| |
| obj = F1() |
| obj.f1() |
| |
| |
| |
| class Base: |
| def f1(self): |
| pass |
| |
| class Foo(Base): |
| def f2(self): |
| pass |
| |
| obj = Foo() |
| |
| obj.f1() |
| obj.f2() |
| |
| |
| class Base: |
| def f1(self): |
| pass |
| |
| class Foo(Base): |
| def f2(self): |
| pass |
| class Bar(base): |
| def f3(self): |
| pass |
| obj1 = Foo() |
| obj2 = Bar() |
| obj1.f1() |
继承关系中的查找顺序:
| |
| class Base: |
| def f1(self): |
| print('base.f1') |
| class Foo(Base): |
| def f2(self): |
| print('foo.f2') |
| obj = Foo() |
| obj.f1() |
| obj.f2() |
| |
| |
| class Base: |
| def f1(self): |
| print('base.f1') |
| class Foo(Base): |
| def f2(self): |
| self.f1() |
| print('foo.f2') |
| def f1(self): |
| print('foo.f1') |
| obj = Foo() |
| obj.f2() |
| |
| |
| |
| |
| class Base: |
| def f1(self): |
| self.f2() |
| print('base.f1') |
| def f2(self): |
| print('base.f2') |
| class Foo(Base): |
| def f2(self): |
| print('foo.f2') |
| obj = Foo() |
| obj.f1() |
| |
| |
| |
| |
| class BaseServer: |
| def serve_forever(self, poll_interval=0.5): |
| self._handle_request_noblock() |
| def _handle_request_noblock(self): |
| self.process_request(request, client_address) |
| |
| def process_request(self, request, client_address): |
| pass |
| |
| class TCPServer(BaseServer): |
| pass |
| |
| class ThreadingMixIn: |
| def process_request(self, request, client_address): |
| pass |
| |
| class ThreadingTCPServer(ThreadingMixIn, TCPServer): |
| pass |
| |
| obj = ThreadingTCPServer() |
| obj.serve_forever() |
7.22.1 super
| - 如果自己和父类都有func,希望自己和父类都调用, |
| super()/指定类名直接调 |
| (在obj对象继承关系范围内找下一个类的func,多继承Base(Foo,Bar))从左到右 |
-
super().方法名():根据self对象所属的类的继承关系,按照顺序依次找方法并执行(找到第一个为止)
| class Base(object): |
| def func(self): |
| super().func() |
| print('base.func') |
| class Bar(object): |
| def func(self): |
| print('bar.func') |
| class Foo(Base,Bar): |
| pass |
| obj = Foo() |
| obj.func() |
| |
| |
| |
7.22.2 mro
| class A: |
| def func(self): |
| print('in a') |
| |
| class B(A): |
| def func(self): |
| print('in b') |
| super().func() |
| |
| class C(A): |
| def func(self): |
| print('in c') |
| super().func() |
| |
| class D(B,C): |
| def func(self): |
| print('in d') |
| super().func() |
| d = D() |
| d.func() |
| print(D.mro()) |
| |
| |
| |
注意事项:
问题:什么时候才能用到继承?多个类中如果有公共方法,可以放到基类中,避免重写。
- self到底是谁?
- self是哪个类创建的(一开始创建的对象obj),就从此类开始找,没有就从父类找
- 多继承关系:多继承,如果一个派生类有多个基类,执行对象.方法时,优先在自己的类中找,如果没有,就从左到右依次从基类中找 要注意self是谁?
7.23 多态(多种形态/多种类型) 也叫鸭子模型
-
一个类表现出的多种状态-->多个类表现出相似的状态
1.基本内容:多种形态/多种类型,python本身就是多态的
| def func(arg): |
| arg.send() |
2.面试题:
对于一个函数而言,python对于参数的类型不会限制,那么传入参数时就可以是各种类型,在函数中如果有arg.send方法,那么就是对于传入类型的一个限制(类型必须有send方法),这就是鸭子模型。
类似于上述的函数我们认为只要能呱呱叫的就是鸭子(只有有send方法,就是我们想要的类型)
7.3 成员
7.3.1 类成员
定义class类时,会在内存中开辟一块空间,但里面的方法/函数不会执行,只有被调用时才会执行

1.类变量:city (静态字段/属性) name在obj对象里
-
定义:写在类的下一级,与方法同级
-
访问:
| 类.类变量名称 obj1.name |
| 对象.类变量名称 |
-
面试题
| class Base: |
| x =1 |
| #实例化对象 |
| obj = Base() |
| obj.x #先去对象中找,如果没有在去类中找 |
| obj.y = 123 #在对象中添加了一个y =123变量。 |
| obj.x = 123 #在对象中添加了一个y =123变量。 |
| print(obj.y) #123 |
| print(obj.x) #123 #只能在自己对象中添加,不会修改类变量中的x |
| print(Base.x) #1 |
-
总结:
- 绑定方法/普通方法
- 定义:至少有一个self参数
- 执行: 先创建对象,由对象.方法()
| class Foo: |
| def __init__(self): |
| self.name = 123 |
| def func(self,a,b): |
| print(self.name,a,b) |
| obj = Foo() |
| obj.func(1,2) |
| |
3.静态方法
-
@staticmethod装饰器
-
参数无限制
执行:
- 类.方法() 直接调用
- 对象.方法() (不推荐)
什么时候用静态方法?
| class Foo: |
| def __init__(self): |
| self.name = 123 |
| def func(self,a,b): |
| print(self.name,a,b) |
| @staticmethod |
| def f1(): |
| print(1234) |
| obj = Foo() |
| obj.func(1,2) |
| Foo.f1() |
| |
4.类方法
定义: (相比静态方法只是多了一个默认参数cls)
- @classmethod装饰器
- 至少有一个cls参数,传的是当前类
执行:
| class Foo: |
| def __init__(self): |
| self.name = 123 |
| def func(self,a,b): |
| print(self.name,a,b) |
| @staticmethod |
| def f1(): |
| print(1234) |
| @classmethod |
| def f2(cls,a,b): |
| print('cls输出的是当前的类',cls) |
| print('a,b') |
| obj = Foo() |
| obj.func(1,2) |
| Foo.f1() |
| Foo.f2(1,2) |
| |
| 示例: |
| class ClassFunc: |
| var1 = "string1" |
| def __init__(self): |
| self.var2 = 'string2' |
| |
| @classmethod |
| def class_func(cls): |
| cls.xx = 'xxxxxxxxxxx' |
| print(str(cls)+" can visit var1:"+cls.var1) |
| print(str(cls)+" can't visit var2") |
| |
| @classmethod |
| def f1(cls): |
| print(cls.xx) |
| |
| |
| ClassFunc.class_func() |
| ClassFunc.f1() |
面试题:
| - 问: @classmethod和@staticmethod的区别? |
| - 答:一个是类方法,一个静态方法 |
| 定义: |
| 类方法:用@classmethod做装饰器且至少有一个cls参数 |
| 静态方法:用staticmethod做装饰器且参数无限制 |
| 调用: |
| 类.方法直接调用,对象.方法也可以调用 |
| |
5.属性
定义:
-
@property装饰器
-
至少有一个self参数
执行:对象.方法 不用加括号了
| class Foo: |
| @property |
| def func(self): |
| print(123) |
| return 666 |
| obj = Foo() |
| result = obj.func |
| print(result) |
| |
| |
| class Page: |
| def __init__(self, total_count, current_page, per_page_count=10): |
| self.total_count = total_count |
| self.per_page_count = per_page_count |
| self.current_page = current_page |
| @property |
| def start_index(self): |
| return (self.current_page - 1) * self.per_page_count |
| @property |
| def end_index(self): |
| return self.current_page * self.per_page_count |
| |
| USER_LIST = [] |
| for i in range(321): |
| USER_LIST.append('alex-%s' % (i,)) |
| |
| |
| current_page = int(input('请输入要查看的页码:')) |
| p = Page(321, current_page) |
| data_list = USER_LIST[p.start_index:p.end_index] |
| for item in data_list: |
| print(item) |
7.3.2 实例(对象)成员
对象里的是实例变量 (字段、属性)
- 就是创建对象obj后,创建的变量name = ‘alex’,name为实例变量
对象的嵌套:
- 函数:参数可以是任意类型
- 字典:对象和类都能当字典的key和value
- 一个类的对象作为另一个类对象的实例变量 (site.registry(3,UserConfig))
| class School(object): |
| def __init__(self,title,addr): |
| self.title = title |
| self.address = addr |
| class ClassRoom(): |
| def __init__(self,name,school_object): |
| self.name =name |
| self.school = school_object |
| |
| s1 = School('北京','沙河') |
| s2 = School('上海','浦东') |
| s3 = School('深圳','南山') |
| |
| c1 = ClassRoom('全栈21',s1) |
| cl.name |
| c1.school.title |
| c1.school.address |
| |
| class StackConfig(object): |
| list_display = '李邵奇' |
| |
| def changelist_view(self): |
| print(self.list_display) |
| |
| class UserConfig(StackConfig): |
| list_display = '利奇航' |
| |
| class AdminSite(object): |
| def __init__(self): |
| self._register = {} |
| |
| def registry(self,key,arg=StackConfig): |
| self._register[key] = arg |
| |
| def run(self): |
| for key,value in self._register.items(): |
| obj = value() |
| obj.changelist_view() |
| site = AdminSite() |
| site.registry(1) |
| site.registry(2,StackConfig) |
| site.registry(3,UserConfig) |
| site.run() |
7.3.3 成员修饰符(继承中的私有成员)
1、公有,所有地方都能访问
2、私有,只有自己才能访问,修饰符: 双下划线__ #加了修饰符后 class 类变量和实例变量都只能在内部访问,也不能在子类中使用
| |
| class Foo: |
| def __init__(self,name): |
| self.__name = name |
| def func(self) |
| print(self.__name) |
| obj = Foo('alex') |
| |
| obj.func() |
| |
| |
| class Foo: |
| __x = 1 |
| @staticmethod |
| def func(): |
| print(Foo.__x) |
| Foo.func() |
| |
| class Foo: |
| def __fun(self): |
| print('msg') |
| def show(self): |
| self.__fun() |
| obj = Foo() |
| |
| obj.show() |
3、强制访问私有,中间加上_类名 (单下划线)
| class Foo(): |
| def __init__(self,name): |
| self.__x = name |
| obj = Foo('alex') |
| print(obj._Foo__x) |
| |
补充:
| class Foo(): |
| pass |
| class Foo(object): |
| pass |
| |
| |
| |
| class Foo: |
| pass |
| |
| class Foo(object): |
| pass |
| |
| |

7.3.4 类成员中的特殊成员 (双下方法/魔术方法/内置方法)
7.3.4.1 __init__
| class Foo(): |
| def __init__(self,name): |
| self.name = name |
| obj = Foo('alex') |
| |
7.3.4.2 __new__
| class Foo(object): |
| def __init__(self): |
| print('初始化对象') |
| self.x = 123 |
| def __new__(cls, *args, **kwargs): |
| print('创建对象') |
| return object.__new__(cls) |
| |
| obj = Foo() |
7.3.4.3 __call__
- 对象后面加括号 执行call方法 。按理来说对象后面不能加()
| class Foo(object): |
| def __call__(self, *args, **kwargs): |
| print('执行call方法') |
| |
| obj = Foo() |
| obj() |
| |
| |
| from wsgiref.simple_server import make_server |
| |
| def func(environ,start_response): |
| start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')]) |
| return ['你好'.encode("utf-8") ] |
| |
| class Foo(object): |
| def __call__(self, environ,start_response): |
| start_response("200 OK", [('Content-Type', 'text/html; charset=utf-8')]) |
| return ['你<h1 style="color:red;">不好</h1>'.encode("utf-8")] |
| |
| |
| server = make_server('127.0.0.1', 8000, Foo()) |
| server.serve_forever() |
| |
7.3.4.4 __getitem__
__setitem
delitem
| class Foo(object): |
| def __setitem__(self, key, value): |
| pass |
| def __getitem__(self, item): |
| return 'uuu' |
| def __delitem__(self, key): |
| pass |
| |
| obj1 = Foo() |
| obj1['k1'] = 123 |
| val = obj1['xxx'] |
| del obj1['ttt'] |
| |
7.3.4.5 __str__
- 只有在打印对象时,会自动调用此方法,并将其返回值在页面显示出来
str('xxxx') 相当于执行的__str__ 方法
| class Foo(object): |
| def __str__(self): |
| return 'asdfasudfasdfsad' |
| |
| obj = Foo() |
| print(obj) |
| |
| class User(object): |
| def __init__(self,name,email): |
| self.name = name |
| self.email = email |
| def __str__(self): |
| return "%s %s" %(self.name,self.email,) |
| |
| user_list = [User('二狗','2g@qq.com'),User('二蛋','2d@qq.com'),User('狗蛋','xx@qq.com')] |
| for item in user_list: |
| print(item) |
| |
7.3.4.6 __dict__
| class Foo(object): |
| def __init__(self,name,age,email): |
| self.name = name |
| self.age = age |
| |
| obj = Foo('alex',19) |
| val = obj.__dict__ |
| print(val) |
| |
7.3.4.7 __enter__ __exit__
| |
| class Foo(object): |
| def do_something(self): |
| print('内部执行') |
| |
| class Context: |
| def __enter__(self): |
| print('进入') |
| return Foo() |
| def __exit__(self, exc_type, exc_val, exc_tb): |
| print('推出') |
| |
| with Context() as ctx: |
| print('内部执行') |
| ctx.do_something() |
7.3.4.8 __add__
| class Foo(object): |
| def __add__(self, other): |
| return 123 |
| |
| obj1 = Foo() |
| obj2 = Foo() |
| val = obj1 + obj2 |
| print(val) |
7.3.4.9 __iter__
| class Foo: |
| def __iter__(self): |
| return iter([1,2,3,4]) |
| |
| obj = Foo() |
7.4 数据结构
7.4.1 队列
| class Queue(object): |
| ''' |
| 先进先出 |
| ''' |
| def __init__(self): |
| self.data_list = [] |
| def push(self,val): |
| self.data_list.insert(0,val) |
| def pop(self): |
| return self.data_list.pop(0) |
| |
7.4.2 栈
| class Stack(object): |
| ''' |
| 后进先出 |
| ''' |
| def __init__(self): |
| self.data_list = [] |
| def push(self,val): |
| ''' |
| 向栈中压入一个数据(入栈) |
| :param val: |
| :return: |
| ''' |
| self.data_list.append(val) |
| def pop(self): |
| ''' |
| 从栈中拿走一个数据(出栈) |
| :return: |
| ''' |
| return self.data_list.pop() |
| |
7.5 约束(抽象类、接口类)
- 又名:Foo抽象类、接口类
- 定义:就是给子类一个规范,让子类必须按照抽象类的规范来实现方法
- 示例
| |
| |
| |
| class BaseMessage(object): |
| def send(self,a1): |
| raise NotmplementedError |
| |
| class Msg(BaseMessage): |
| def senf(self): |
| pass |
| class Email(BassMessage): |
| def send(self): |
| pass |
| class DingDing(BaseMessage): |
| def send(self): |
| pass |
| obj = Email() |
| obj.send() |
| |
7.6 反射
- 根绝字符串的形式去某个对象中操作他的成员 (这里的对象可以是类,模块,py文件)
- python中一切皆为对象(模块,包,类,对象)
反射:
| 通过 对象 来获取 实例变量、绑定方法 |
| 通过 类 来获取类变量、类方法、静态方法 |
| 通过 模块名 来获取 模块中的任意变量(普通变量、函数。类) |
| 通过 本文件 来获取本文件的任意变量 |
| |
| |

7.6.1 getattr (对象,字符串)
| class Foo(object): |
| def __init__(self,name): |
| self.name = name |
| def login(self): |
| print('登录') |
| obj = Foo('alex') |
| |
| v1 =getattr(obj,'name') |
| print(v1) |
| |
| method_name = getattr(obj,'login') |
| print(method_name()) |
| |
| |
| |
| |
| class Foo(object): |
| def get(self): |
| pass |
| |
| obj = Foo() |
| |
| |
| |
| v1 = getattr(obj,'get',None) |
| print(v1) |
| |
7.6.2 hasattr (对象,字符串)
| from wsgiref.simple_server import make_server |
| class View(object): |
| def login(self): |
| return '登陆' |
| def logout(self): |
| return '登出' |
| def index(self): |
| return '首页' |
| |
| def func(environ,start_response): |
| start_response("200 OK", [('Content-Type', 'text/plain; charset=utf-8')]) |
| obj = View() |
| |
| method_name = environ.get('PATH_INFO').strip('/') |
| if not hasatt(obj.method_name): |
| return ["sdf".encode("utf-8"),] |
| response = getattr(obj,method_name)() |
| return [response.encode("utf-8") ] |
| |
| |
| server = make_server('192.168.12.87', 8000, func) |
| server.serve_forever() |
7.6.3 setattr (对象,字符串,值)
| class Foo(object): |
| pass |
| obj = Foo() |
| obj.k1 = 999 |
| setattr(obj,'k1',123) |
| print(obj.k1) |
7.6.4 delattr (对象,字符串)
| class Foo: |
| pass |
| |
| obj = Foo() |
| obj.k1 = 999 |
| delattr(obj,'k1') |
| print(obj.k1) |
| |
7.6.5 python一切皆对象
- 对象的定义
- 狭义:类实例化出来的就是对象
- 广义:
- 模块(py文件)及其内部成员
- 包(文件夹)及其内部成员
- 类及其内部成员
- 狭义的对象
- python一切皆对象,所以以后想要通过字符串的形式操作其内部成员都可以通过反射的机制实现
7.7 单例模式(设计模式)
1.单例模式(23中设计模式)
| |
| class Foo(): |
| pass |
| obj1 = Foo() |
| obj2 = Foo() |
| |
| |
| obj1 = Foo() |
| obj2 = Foo() |
| |
| class Singleton(object): |
| instance = None |
| def __new__(cls, *args, **kwargs): |
| |
| |
| |
| |
| |
| if not cls.instance: |
| cls.instance = object.__new__(cls) |
| return cls.instance |
| |
| obj1 = Singleton() |
| obj2 = Singleton() |
| |
| |
| |
| class FileHelper(object): |
| instance = None |
| def __init__(self, path): |
| self.file_object = open(path,mode='r',encoding='utf-8') |
| |
| def __new__(cls, *args, **kwargs): |
| if not cls.instance: |
| cls.instance = object.__new__(cls) |
| return cls.instance |
| |
| obj1 = FileHelper('x') |
| obj2 = FileHelper('x') |
| |
| import jd |
| import jd |
| print(456) |
| |
| |
| import importlib |
| importlib.reload(jd) |
| |
| class Foo(object): |
| pass |
| obj = Foo() |
| |
| |
| import jd |
| print(jd.obj) |
| |
7.8 项目结果目录
7.8.1 项目分类
- 脚本:把所有代码写在一个py文件中

-
单可执行文件:只有一个可执行文件
| |
| if __name__ == '__main__': |
| pass |
-
多可执行文件:有多个可执行文件
7.8.2 项目目录
- 包及其内含文件示例
- 可执行文件:bin
- 将所有可执行文件放入其中 #注意要把项目路径加到sys.path中
- 业务相关:src
- 登陆注册相关:account.py
- 订单相关:order.py
- 主程序:run.py
- 公共类库:lib
- 数据库:db
- 配置:config
- 日志存储:log

-
注意:每个可执行文件必须把项目根目录加入sys.path中
| import os |
| import sys |
| |
| BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| sys.path.append(BASE_DIR) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)