首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Python-VBA函数之旅-property函数

  • 25-03-03 04:31
  • 3249
  • 9158
blog.csdn.net

目录

一、property函数的常见应用场景

二、property函数使用注意事项

三、如何用好property函数?

1、property函数:

1-1、Python:

1-2、VBA:

2、推荐阅读:

个人主页:神奇夜光杯-CSDN博客 

  

一、property函数的常见应用场景

        在Python中,property()函数(或更常见的是@property装饰器)的实际应用场景非常广泛,主要用于封装类的内部状态并提供对外的访问接口,常见的应用场景有:

1、隐藏内部实现细节:通过getter和setter方法,我们可以隐藏对象的内部状态,只暴露必要的接口给外部调用者,这有助于保护对象的状态不被随意修改,同时也使得代码更加易于理解和维护。

2、添加属性验证:在setter方法中,我们可以添加对属性值的验证逻辑。例如,我们可以确保一个年龄属性总是大于0,或者一个邮箱地址属性总是符合某种格式。

3、计算属性:有些属性可能不是直接存储在对象的状态中的,而是需要基于其他属性计算得出,通过使用property,我们可以将这样的属性表示为对象的一个“正常”属性,而无需调用一个方法来获取它。

4、延迟计算:对于某些计算成本较高的属性,我们可能希望在第一次访问时才进行计算,并将结果缓存起来,通过使用property和内部变量,我们可以实现这样的延迟计算。

5、访问控制: property()函数允许你控制对属性的访问权限。例如,你可以使属性只读,或者只允许在特定条件下修改属性。

6、简化接口:在某些情况下,你可能想要隐藏对象的内部复杂性,只向外部提供一个简洁的接口,使用property可以将内部方法(如getter、setter)暴露为看起来像普通属性的接口。

7、实现数据绑定:在更复杂的场景中,property可以与观察者模式(Observer Pattern)或数据绑定库结合使用,以实现当属性发生变化时自动触发某些操作或更新UI等效果。

8、线程安全:在多线程环境中,对属性的直接访问可能会导致数据竞争或其他并发问题,使用property可以确保在访问或修改属性时执行必要的同步操作,从而保证线程安全。

9、日志记录或监控:在获取或设置属性时,你可能想要记录一些信息(如访问时间、访问者等),或者触发某些监控事件,通过property可以轻松实现这些功能。

10、惰性加载:对于某些需要大量计算或需要从外部资源(如数据库或网络)加载的属性,可以使用property实现惰性加载,这意味着属性只有在首次被访问时才会进行计算或加载,从而节省资源。

二、property函数使用注意事项

        在Python中,property()函数是一个内置函数,用于在新式类中返回属性值;property()函数允许你定义getter,setter和deleter方法,这些方法分别用于获取、设置和删除对象的属性。以下是在使用property()函数时需要注意的一些事项:

1、定义getter/setter/deleter方法:getter方法通常不需要任何参数,并返回属性的值;setter方法需要接收两个参数:self和要设置的值value;deleter方法只需要self参数。   

  1. 示例:
  2.    class Circle:
  3.        def __init__(self, radius=1.0):
  4.            self._radius = radius
  5.        @property
  6.        def radius(self):
  7.            return self._radius
  8.        @radius.setter
  9.        def radius(self, value):
  10.            if value < 0:
  11.                raise ValueError("Radius cannot be negative")
  12.            self._radius = value
  13.        @radius.deleter
  14.        def radius(self):
  15.            del self._radius

2、私有属性:当你使用@property装饰器时,通常建议将实际存储数据的属性设为私有(例如使用下划线前缀),以避免直接访问和修改它们。

3、装饰器语法:你可以使用装饰器语法来定义getter,setter和deleter方法;另一种语法是使用property()函数直接返回getter,setter和deleter方法作为元组,但这种方法较少使用,因为它不够直观。

4、性能:虽然property()通常不会成为性能瓶颈,但在需要处理大量对象的场合中,频繁地调用getter或setter可能会产生一些开销,在这种情况下,你可能需要考虑其他方法,如使用@cached_property装饰器(来自cached_property库或类似库)来缓存getter方法的返回值。

5、线程安全:如果你在多线程环境中使用setter或deleter方法,并且这些方法可能会修改共享状态,那么你需要确保这些方法是线程安全的。

6、错误处理:在setter方法中,你可能需要添加错误处理逻辑来确保设置的值是有效的。

7、文档字符串:为getter,setter和deleter方法提供文档字符串是一个好习惯,这有助于其他开发者理解这些方法的作用和用法。

8、可读性:当使用property()函数时,确保你的getter,setter和deleter方法的命名清晰易懂,以便其他开发者能够轻松理解你的代码。

9、继承:虽然property()函数提供了更好的封装和抽象,但它也带来了一定的开销,因为每次访问属性时都会调用一个方法,然而,在大多数情况下,这种开销是可以接受的,因为Python方法调用的开销相对较低。

10、向后兼容性:如果你的类需要向后兼容Python 2,请注意property()函数在Python 2和Python 3中的行为可能略有不同:在Python 2中,property()是一个内置的工厂函数;而在Python 3中,它不仅是一个内置函数,也可以用作装饰器。

三、如何用好property函数?

        在Python中,property()函数是一个内置函数,用于创建只读的、可写的或可删除的属性,这提供了一种将方法作为属性访问的方式,从而简化了对对象属性的访问和修改。

property()函数的基本用法有三种:

1、只读属性:只需要定义一个getter方法。

  1. class MyClass:
  2.     def __init__(self, value):
  3.         self._value = value
  4.     @property
  5.     def value(self):
  6.         return self._value
  7. obj = MyClass(10)
  8. print(obj.value)  # 输出: 10

在这个例子中,`value` 是一个只读属性,只能通过 `obj.value` 来访问,但不能直接赋值。

2、可读写属性:除了getter方法,还需要定义一个setter方法。

  1. class MyClass:
  2.     def __init__(self, value):
  3.         self._value = value
  4.     @property
  5.     def value(self):
  6.         return self._value
  7.     @value.setter
  8.     def value(self, new_value):
  9.         if new_value < 0:
  10.             raise ValueError("Value must be non-negative")
  11.         self._value = new_value
  12. obj = MyClass(10)
  13. print(obj.value)  # 输出: 10
  14. obj.value = 20
  15. print(obj.value)  # 输出: 20
  16. obj.value = -1   # 抛出 ValueError

在这个例子中,`value` 是一个可读写属性,可以通过 `obj.value` 来访问,也可以通过 `obj.value = new_value` 来修改,在setter方法中,我们还添加了一个简单的验证逻辑。

3、可删除属性:除了getter和setter方法,还需要定义一个deleter方法。

  1. class MyClass:
  2.     def __init__(self, value):
  3.         self._value = value
  4.     @property
  5.     def value(self):
  6.         return self._value
  7.     @value.setter
  8.     def value(self, new_value):
  9.         self._value = new_value
  10.     @value.deleter
  11.     def value(self):
  12.         del self._value
  13. obj = MyClass(10)
  14. print(obj.value)  # 输出: 10
  15. obj.value = 20
  16. print(obj.value)  # 输出: 20
  17. del obj.value
  18. # 尝试访问 obj.value 将引发 AttributeError

在这个例子中,`value` 是一个可删除的属性,可以使用 `del obj.value` 来删除它。

注意:虽然property()函数可以直接使用,但通常建议使用@property、@value.setter和 @value.deleter装饰器来定义属性,因为它们更加简洁和易读。

1、property函数:
1-1、Python:
  1. # 1.函数:property
  2. # 2.功能:用于创建属性实例,这些实例可以绑定到类的方法上,从而允许我们像访问数据属性一样访问类的方法
  3. # 3.语法:property([fget=None[, fset=None[, fdel=None[, doc=None]]]])
  4. # 4.参数:
  5. # 4-1、fget:用于获取属性值的方法(getter),它应该是一个不带参数的方法,并且返回一个值;如果省略,则属性为只读
  6. # 4-2、fset:用于设置属性值的方法(setter),它应该是一个接受一个参数(新值)的方法,并且不返回任何内容(或者返回None);如果省略,则属性为只读
  7. # 4-3、fdel:用于删除属性的方法(deleter),它应该是一个不带参数的方法,并且不返回任何内容(或者返回None);如果省略,则不能删除属性
  8. # 4-4、doc:一个可选的字符串,作为属性的文档字符串,如果省略,则使用`fget`、`fset`或`fdel`方法中的文档字符串(如果存在的话)
  9. # 5.返回值:返回类的属性
  10. # 6.说明:
  11. # 6-1、如果给出doc参数,doc将成为该property属性的文档字符串;否则该property将复制fget/fset/fdel等方法中的文档字符串(如果存在的话)
  12. # 7.示例:
  13. # 用dir()函数获取该函数内置的属性和方法
  14. print(dir(property))
  15. # ['__class__', '__delattr__', '__delete__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__',
  16. # '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__isabstractmethod__', '__le__', '__lt__',
  17. # '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__set__', '__set_name__', '__setattr__', '__sizeof__',
  18. # '__str__', '__subclasshook__', 'deleter', 'fdel', 'fget', 'fset', 'getter', 'setter']
  19. # 用help()函数获取该函数的文档信息
  20. help(property)
  21. # 应用一:隐藏内部实现细节
  22. class Circle:
  23. def __init__(self, radius):
  24. self._radius = radius
  25. @property
  26. def radius(self):
  27. """Getter for radius."""
  28. return self._radius
  29. @radius.setter
  30. def radius(self, value):
  31. """Setter for radius, checks if value is positive."""
  32. if value < 0:
  33. raise ValueError("Radius cannot be negative.")
  34. self._radius = value
  35. @property
  36. def diameter(self):
  37. """Calculate and return the diameter."""
  38. return 2 * self._radius
  39. @property
  40. def area(self):
  41. """Calculate and return the area."""
  42. import math
  43. return math.pi * (self._radius ** 2)
  44. # 注意:diameter和area是只读的,因为它们没有setter方法
  45. # 使用Circle类
  46. c = Circle(5)
  47. print(c.radius) # 访问radius属性
  48. print(c.diameter) # 访问diameter属性(只读)
  49. print(c.area) # 访问area属性(只读)
  50. # 5
  51. # 10
  52. # 78.53981633974483
  53. c.radius = 10 # 修改radius属性
  54. print(c.radius) # 再次访问radius属性,确认已修改
  55. print(c.diameter) # 再次访问diameter属性,确认已根据新的radius更新
  56. print(c.area) # 再次访问area属性,确认已根据新的radius更新
  57. # 10
  58. # 20
  59. # 314.1592653589793
  60. # 尝试设置直径或面积(这将失败,因为它们没有setter方法)
  61. # c.diameter = 15 # 这会抛出一个 AttributeError
  62. # c.area = 50 # 这也会抛出一个 AttributeError
  63. # AttributeError: property 'diameter' of 'Circle' object has no setter
  64. # 应用二:添加属性验证
  65. class Person:
  66. def __init__(self, name, age):
  67. self._name = name
  68. self._age = None
  69. self.age = age # 使用setter方法进行初始化
  70. @property
  71. def name(self):
  72. return self._name
  73. @property
  74. def age(self):
  75. """Getter for age."""
  76. return self._age
  77. @age.setter
  78. def age(self, value):
  79. """Setter for age, checks if value is a positive integer."""
  80. if not isinstance(value, int) or value < 0:
  81. raise ValueError("Age must be a positive integer.")
  82. self._age = value
  83. # 如果你想要一个deleter方法,可以添加如下:
  84. @age.deleter
  85. def age(self):
  86. raise AttributeError("Cannot delete the age attribute.")
  87. # 使用 Person 类
  88. try:
  89. p = Person("Myelsa", 18)
  90. print(p.name) # 输出: Myelsa
  91. print(p.age) # 输出: 18
  92. p.age = 42 # 修改age属性
  93. print(p.age) # 输出: 42
  94. # 尝试设置不合法的age值
  95. p.age = -1 # 抛出 ValueError
  96. # p.age = "fourty two" # 也会抛出ValueError
  97. except ValueError as e:
  98. print(e)
  99. # Myelsa
  100. # 18
  101. # 42
  102. # Age must be a positive integer.
  103. # 尝试删除age属性(如果已定义deleter)
  104. del p.age # 如果 deleter 存在且未抛出AttributeError,则会执行
  105. # 否则,上面的代码会按原样运行,因为deleter抛出了AttributeError
  106. # AttributeError: Cannot delete the age attribute.
  107. # 应用三:计算属性
  108. class Rectangle:
  109. def __init__(self, width, height):
  110. self._width = width
  111. self._height = height
  112. @property
  113. def width(self):
  114. """Getter for width."""
  115. return self._width
  116. @width.setter
  117. def width(self, value):
  118. """Setter for width."""
  119. if value < 0:
  120. raise ValueError("Width cannot be negative.")
  121. self._width = value
  122. @property
  123. def height(self):
  124. """Getter for height."""
  125. return self._height
  126. @height.setter
  127. def height(self, value):
  128. """Setter for height."""
  129. if value < 0:
  130. raise ValueError("Height cannot be negative.")
  131. self._height = value
  132. @property
  133. def area(self):
  134. """Calculate and return the area."""
  135. return self._width * self._height
  136. @property
  137. def perimeter(self):
  138. """Calculate and return the perimeter."""
  139. return 2 * (self._width + self._height)
  140. # 使用Rectangle类
  141. rect = Rectangle(5, 10)
  142. print(rect.area) # 输出: 50 (宽度5乘以高度10)
  143. print(rect.perimeter) # 输出: 30 (两倍的(宽度5加高度10))
  144. rect.width = 7
  145. print(rect.area) # 输出: 70 (新的宽度7乘以高度10)
  146. print(rect.perimeter) # 输出: 34 (两倍的(新的宽度7加高度10))
  147. # 50
  148. # 30
  149. # 70
  150. # 34
  151. # 应用四:延迟计算
  152. class Circle:
  153. def __init__(self, radius):
  154. self._radius = radius
  155. self._area = None
  156. self._circumference = None
  157. @property
  158. def radius(self):
  159. """Getter for radius."""
  160. return self._radius
  161. @radius.setter
  162. def radius(self, value):
  163. """Setter for radius."""
  164. if value < 0:
  165. raise ValueError("Radius cannot be negative.")
  166. self._radius = value
  167. # 当半径改变时,重置缓存的area和circumference
  168. self._area = None
  169. self._circumference = None
  170. @property
  171. def area(self):
  172. """Calculate and cache the area."""
  173. if self._area is None:
  174. self._area = 3.14159 * self._radius ** 2
  175. return self._area
  176. @property
  177. def circumference(self):
  178. """Calculate and cache the circumference."""
  179. if self._circumference is None:
  180. self._circumference = 2 * 3.14159 * self._radius
  181. return self._circumference
  182. # 使用 Circle 类
  183. circle = Circle(5)
  184. print(circle.area) # 首次访问,进行计算并缓存结果
  185. print(circle.circumference) # 首次访问,进行计算并缓存结果
  186. # 再次访问,直接从缓存中获取结果
  187. print(circle.area) # 输出与之前相同,因为结果是缓存的
  188. print(circle.circumference) # 输出与之前相同,因为结果是缓存的
  189. circle.radius = 7
  190. print(circle.area) # 半径改变,重新计算并缓存新的area
  191. print(circle.circumference) # 半径改变,重新计算并缓存新的circumference
  192. # 78.53975
  193. # 31.4159
  194. # 78.53975
  195. # 31.4159
  196. # 153.93791
  197. # 43.98226
  198. # 应用五:访问控制
  199. class AccessControlled:
  200. def __init__(self, value):
  201. self._value = value
  202. def get_value(self):
  203. """Getter for value."""
  204. # 在这里可以添加访问前的验证或逻辑
  205. print("Reading value...")
  206. return self._value
  207. def set_value(self, value):
  208. """Setter for value."""
  209. # 在这里可以添加设置前的验证或逻辑
  210. if value < 0:
  211. raise ValueError("Value cannot be negative.")
  212. print("Setting value...")
  213. self._value = value
  214. def del_value(self):
  215. """Deleter for value."""
  216. # 在这里可以添加删除前的验证或逻辑
  217. print("Deleting value...")
  218. del self._value
  219. # 使用property()将方法绑定到属性
  220. value = property(get_value, set_value, del_value, "This is a controlled access value.")
  221. # 使用AccessControlled类
  222. obj = AccessControlled(10)
  223. # 读取属性值
  224. print(obj.value) # 输出: Reading value... 10
  225. # 设置属性值
  226. obj.value = 20
  227. # 输出: Setting value...
  228. # 尝试设置不合法的属性值
  229. try:
  230. obj.value = -5
  231. except ValueError as e:
  232. print(e) # 输出: Value cannot be negative.
  233. # 删除属性值
  234. del obj.value
  235. # 输出: Deleting value...
  236. # 尝试访问已删除的属性值
  237. try:
  238. print(obj.value)
  239. except AttributeError:
  240. print("Value has been deleted.") # 输出: Value has been deleted.
  241. # Reading value...
  242. # 10
  243. # Setting value...
  244. # Value cannot be negative.
  245. # Deleting value...
  246. # Reading value...
  247. # Value has been deleted.
  248. # 应用六:简化接口
  249. class Rectangle:
  250. def __init__(self, width, height):
  251. self._width = width
  252. self._height = height
  253. @property
  254. def width(self):
  255. """Getter for width."""
  256. return self._width
  257. @width.setter
  258. def width(self, value):
  259. """Setter for width."""
  260. if value < 0:
  261. raise ValueError("Width cannot be negative.")
  262. self._width = value
  263. @property
  264. def height(self):
  265. """Getter for height."""
  266. return self._height
  267. @height.setter
  268. def height(self, value):
  269. """Setter for height."""
  270. if value < 0:
  271. raise ValueError("Height cannot be negative.")
  272. self._height = value
  273. @property
  274. def area(self):
  275. """Calculate and return the area of the rectangle."""
  276. return self._width * self._height
  277. @property
  278. def perimeter(self):
  279. """Calculate and return the perimeter of the rectangle."""
  280. return 2 * (self._width + self._height)
  281. # 使用 Rectangle 类
  282. rect = Rectangle(5, 10)
  283. # 访问属性,无需关心内部计算
  284. print(rect.area) # 输出: 50
  285. print(rect.perimeter) # 输出: 30
  286. # 设置属性
  287. rect.width = 7
  288. rect.height = 8
  289. # 再次访问属性
  290. print(rect.area) # 输出: 56
  291. print(rect.perimeter) # 输出: 30
  292. # 尝试设置不合法的属性值
  293. try:
  294. rect.width = -5
  295. except ValueError as e:
  296. print(e) # 输出: Width cannot be negative.
  297. # 50
  298. # 30
  299. # 56
  300. # 30
  301. # Width cannot be negative.
  302. # 应用七:实现数据绑定
  303. class DataBinder:
  304. def __init__(self, initial_value, callback=None):
  305. self._value = initial_value
  306. self._callback = callback
  307. @property
  308. def value(self):
  309. """Getter for value."""
  310. return self._value
  311. @value.setter
  312. def value(self, new_value):
  313. """Setter for value. Triggers the callback if provided."""
  314. self._value = new_value
  315. if self._callback:
  316. self._callback(new_value)
  317. # 定义一个回调函数来模拟数据绑定的效果
  318. def on_value_change(new_value):
  319. print(f"Value has changed to: {new_value}")
  320. # 使用 DataBinder 类并设置回调函数
  321. binder = DataBinder(10, on_value_change)
  322. # 访问属性值
  323. print(binder.value) # 输出: 10
  324. # 修改属性值,这会触发回调函数
  325. binder.value = 20
  326. # 输出: Value has changed to: 20
  327. # 再次修改属性值
  328. binder.value = 30
  329. # 输出: Value has changed to: 30
  330. # 应用八:线程安全
  331. import threading
  332. class ThreadSafeClass:
  333. def __init__(self):
  334. self._value = 0
  335. self._lock = threading.Lock()
  336. @property
  337. def value(self):
  338. """线程安全的getter方法"""
  339. with self._lock:
  340. return self._value
  341. @value.setter
  342. def value(self, new_value):
  343. """线程安全的setter方法"""
  344. with self._lock:
  345. self._value = new_value
  346. # 使用示例
  347. if __name__ == "__main__":
  348. # 创建一个ThreadSafeClass的实例
  349. obj = ThreadSafeClass()
  350. # 假设我们有两个线程,它们将尝试同时访问和修改obj.value
  351. def worker(obj, name, increment):
  352. for _ in range(100000): # 假设我们进行大量操作以突出线程安全问题
  353. with obj._lock: # 在这里,我们只是为了演示而手动加锁,但在实际setter/getter中不需要
  354. old_value = obj.value
  355. new_value = old_value + increment
  356. obj.value = new_value
  357. print(f"{name}: Value after increment is {obj.value}")
  358. # 创建两个线程
  359. t1 = threading.Thread(target=worker, args=(obj, "Thread 1", 1))
  360. t2 = threading.Thread(target=worker, args=(obj, "Thread 2", -1))
  361. # 启动线程
  362. t1.start()
  363. t2.start()
  364. # 等待线程完成
  365. t1.join()
  366. t2.join()
  367. # 输出最终值(可能不是0,因为线程调度是随机的,但每次操作都是线程安全的)
  368. print(f"Final value: {obj.value}")
  369. # 应用九:日志记录或监控
  370. import logging
  371. class LoggedPropertyClass:
  372. def __init__(self):
  373. self._value = None
  374. self._logger = logging.getLogger(__name__)
  375. self._logger.setLevel(logging.INFO)
  376. # 创建一个handler,用于写入日志文件
  377. fh = logging.FileHandler('property_log.log')
  378. fh.setLevel(logging.INFO)
  379. # 再创建一个handler,用于输出到控制台
  380. ch = logging.StreamHandler()
  381. ch.setLevel(logging.INFO)
  382. # 定义handler的输出格式
  383. formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
  384. fh.setFormatter(formatter)
  385. ch.setFormatter(formatter)
  386. # 给logger添加handler
  387. self._logger.addHandler(fh)
  388. self._logger.addHandler(ch)
  389. @property
  390. def value(self):
  391. """Getter方法,用于访问_value属性,并添加日志记录"""
  392. self._logger.info("Accessing value: %s", self._value)
  393. return self._value
  394. @value.setter
  395. def value(self, new_value):
  396. """Setter方法,用于设置_value属性,并添加日志记录"""
  397. self._logger.info("Setting value to: %s", new_value)
  398. self._value = new_value
  399. # 使用示例
  400. if __name__ == "__main__":
  401. obj = LoggedPropertyClass()
  402. obj.value = 42 # 这将触发setter方法并记录日志
  403. print(obj.value) # 这将触发getter方法并记录日志
  404. # 42
  405. # 2024-05-07 18:46:57,919 - __main__ - INFO - Setting value to: 42
  406. # 2024-05-07 18:46:57,919 - __main__ - INFO - Accessing value: 42
  407. # 应用十:惰性加载
  408. class LazyLoadedClass:
  409. def __init__(self):
  410. # 初始化时,我们不立即加载数据,而是设置一个标记
  411. self._data = None
  412. self._loaded = False
  413. @property
  414. def data(self):
  415. """惰性加载的getter方法"""
  416. if not self._loaded:
  417. # 模拟数据加载过程,这里可以替换为实际的数据加载逻辑
  418. self._data = self._load_data()
  419. self._loaded = True
  420. return self._data
  421. def _load_data(self):
  422. """模拟的数据加载函数"""
  423. # 这里可以放置实际的加载逻辑,比如从数据库、文件或网络请求中获取数据
  424. # 这里我们只是简单地返回一个固定的值作为示例
  425. print("Loading data...")
  426. return "Loaded data"
  427. # 使用示例
  428. if __name__ == "__main__":
  429. obj = LazyLoadedClass()
  430. print(obj.data) # 第一次访问,会加载数据并打印"Loading data...",然后返回"Loaded data"
  431. print(obj.data) # 第二次访问,因为数据已经被加载和缓存,所以不会再次加载,直接返回"Loaded data"
  432. # Loading data...
  433. # Loaded data
  434. # Loaded data
1-2、VBA:
略,待后补。
2、推荐阅读:

2-1、Python-VBA函数之旅-open()函数

Python算法之旅:Algorithm

Python函数之旅:Functions

个人主页:神奇夜光杯-CSDN博客 
文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览61139 人正在系统学习中
遨游码海,我心飞扬
微信名片
注:本文转载自blog.csdn.net的神奇夜光杯的文章"https://myelsa1024.blog.csdn.net/article/details/138501443"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top