文章目录

  • 面向对象
    • 封装
    • 继承
    • 组合
    • 构造函数 init
    • 重写
    • super
    • Mix-in
    • 多态
    • 私有变量、私有方法和__slots__
      • 私有变量
      • 私有方法
      • __ slots __
    • 魔法方法(类的生命周期钩子)
      • __ new __ (cls,...)
      • __ del __ (self)
      • 对象的重生
        • 全局变量重生
        • 闭包重生
      • 属性相关的魔法方法(hasattr()、getattr()、setattr()、delattr())
        • getattr和hasattr 的魔法方法 __ getattribute __ 、 __ getattr __
        • setattr 的魔法方法__ setattr __
          • super 避免递归
          • __ dict __ 避免递归
        • delattr 的魔法方法__ delattr __
      • 索引、切片、迭代协议
        • 索引和切片
        • 迭代协议
      • in 和 not in 的魔法方法__ contains __
      • 比较运算符
    • property()和描述符
      • property
      • 描述符
    • 类方法和静态方法
      • 类方法
      • 静态方法
      • 对比

面向对象

封装

面向对象也是一种代码 封装 的方法

对象 = 属性(静态特征) + 方法(能做的事)

创建对象前通过类将相关的属性和方法打包到一起,然后通类来生成相印的对象

好比工厂在批量生产前会先制作一个模具,而类就是这个模具

首先我们需要创建一个类(class),类可以理解成摸具,更具这个摸具我们可以创造很多相似的对象。

class Animal():eyes = 2legs = 4def run(self):print("我在运动")def call(self):print("我在叫")

使用我们的类创建对象并使用内部方法和属性

...a1 = Animal()
a1.run()  # 我在运动
a1.call()  # 我在叫
print(a1.legs)  # 4
a1.legs = 3
print(a1.legs)  # 3

两个对象之间互不干扰

a1 = Animal()
a2 = Animal()
a1.legs = 3
print(a1.legs)  # 3
print(a2.legs)  # 4

继承

可以使用现有类的所有功能,并且在无需重新编写代码的情况下对这些代码进行扩展

通过继承创建的新类我们称之为子类,被继承的类称之为父类或者基类

# 父类
class Animal():eyes = 2legs = 4def run(self):print("我在运动")def call(self):print("我在叫")class Cat(Animal):def call(self):print("猫在叫")c = Cat()
c.run()
c.call()
  • isinstance()

    • 判断对象是不是属于某个类
  • issubclass()
    • 检测一个类是否为某个类的字类
...print(isinstance(c, Cat))  # T
print(isinstance(c, Animal))  # T
print(issubclass(Cat, Animal))  # T
print(issubclass(Animal, Cat))  # F

组合

我们可以把不相关的类放到另外的一个类中一起调用

class Animal():eyes = 2legs = 4name = ""def run(self):print("我在运动")def call(self):print("我在说话")class Cat(Animal):name = "猫"varieties = ""def call(self):print("喵喵喵")class Dog(Animal):name = "狗"def call(self):print("油猴!")class PetShop():c = Cat()d = Dog()def call(self):self.c.call()self.d.call()pet = PetShop()
pet.call()
"""喵喵喵油猴!
"""

构造函数 init

我们在编写类的时候可以使用 init 来接收外部传入的参数

class Class():def __init__(self, x, y):self.x = xself.y = ydef add(self):print(self.x + self.y)def mul(self):print(self.x * self.y)c = Class(4, 5)c.add()
c.mul()

重写

如果我们对于父类的某个属性或某个方法不满意的话完全可以重写写一个同名的属性或方法对其进行覆盖这种行为我们称之为 重写

class Animal():eyes = 2legs = 4def run(self):print("我在运动")def call(self):print("我在叫")class Cat(Animal):def call(self):print("猫在叫")

如上代码所示,猫类继承动物类,内部有和动物类同名的方法,这样就实现了猫类对动物类call方法的重写

super

我们可以使用super调用父类中的方法,这样更安全。

class Animal():def __init__(self, eyes, legs):self.eyes = eyesself.legs = legsdef run(self):print("我在运动")def call(self):print("我在叫")class Cat(Animal):def __init__(self, eyes, legs):super(Cat, self).__init__(eyes, legs)def call(self):print("喵喵喵")super(Cat, self).call()c = Cat(4, 5)
c.call()
"""喵喵喵我在叫
"""

Mix-in

这个概念类似于插件,在不改变原本代码的情况下利用多继承让类多出一些功能

class Animal():def __init__(self, eyes, legs):self.eyes = eyesself.legs = legsdef run(self):print("我在运动")def call(self):print("我在叫")class Cat(Animal):def __init__(self, eyes, legs):super(Cat, self).__init__(eyes, legs)def call(self):print("喵喵喵")super(Cat, self).call()

如上代码所示,我现在想让猫类可以飞,如何最小的改动代码呢?

...class FlyMixin():def fly(self):print("我会飞")class Cat(FlyMixin, Animal):...

只需要在猫类前创建一个飞的mixin 像插件一样直接继承就可以了

多态

当我们使用 + 和 * 的时候我们会发现当两边的类型发生变化时,结果也会发生变化

print(1 + 1)
print("1" + "1")

有类似特征的还有 len()

len("123")
len(['123'])

这种传入不同类型数据,返回不同结果的性质我们叫做多态。

首先我们要知道我门传入的都是对象,不同的对象。我们来构建类似的方法

class Animal():def __init__(self, name, age):self.name = nameself.age = agedef show(self):print(f"我是{self.name},今年{self.age}")class Cat(Animal):passclass Dog(Animal):passclass Pig(Animal):passc = Cat('cat', '1')
d = Dog('dog', '1')
p = Pig('pig', '1')def animal(obj):obj.show()animal(c)
animal(d)
animal(p)

私有变量、私有方法和__slots__

私有变量

我们都知道,class中的属性和方法,外部都可以通过对象直接调用,如果们不想被直接调用可以这样写

# coding=utf-8
class A():def __init__(self, a):self.__a = adef get_a(self):return self.__adef set_a(self, a):self.__a = areturn self.__aa = A(1)
# print(a.__a)  # AttributeError: 'A' object has no attribute '__a'
print(a.get_a())    # 1
print(a.set_a(2))   # 2

我们这样就只能通过我们指定的接口来访问了。但真的如此么?

...
print(a.__dict__)   # {'_A__a': 2}
print(a._A__a)  # 2

我们发现其实__a 只是被换了名字。

私有方法

私有方法就是在方法名前去添加__

...def __b_func(self):print("func")a._A__b_func()  # func

__ slots __

为什么python的对象可以动态的让我们添加属性呢?是因为对象的属性由dict存放.

class A():def __init__(self, a):self.a = aa = A(1)
print(a.__dict__)   # {'a': 1}
a.z = 30
print(a.__dict__)   # {'a': 1, 'z': 30}

我们甚至可以通过修改这个__ dict __ 来修改对象的属性

class A():def __init__(self, a):self.a = aa = A(1)
print(a.__dict__)  # {'a': 1}
a.z = 30
print(a.__dict__)  # {'a': 1, 'z': 30}
a.__dict__['z'] = 50
print(a.z)  # 50
a.__dict__['un'] = 55
print(a.un)  # 55

如果我们不需要动态修改对象的属性这样会造成内存空间的浪费,我们可以使用 __ slots __

class A():__slots__ = ["a", "b"]def __init__(self, a):self.a = aa = A(1)
a.b = 4
a.c = 3 # AttributeError: 'A' object has no attribute 'c'

魔法方法(类的生命周期钩子)

魔法方法: 在类中不需要我们显性调用的方法就是魔法方法 比如 __ init __ ()

__ new __ (cls,…)

new 也是一个魔法方法,他的作用就是创建一个类的实例,将其传递给 init 方法。 self 就是其返回的

class CapStr(str):def __new__(cls, string):string = string.upper()return super().__new__(cls, string)c = CapStr("python")
print(c)  # PYTHON

__ del __ (self)

del 在对象被销毁后会调用

class C():def __init__(self):print("我来拉~")def __del__(self):print("我走拉~")c = C()  # 我来拉~
del c  # 我走拉~

Python的垃圾回收机制,当一个对象没有任何引用的时候才会将其销毁。

也就是说 我们直接使用 del 删除可能不会触发 __ del __

class C():def __init__(self):print("我来拉~")def __del__(self):print("我走拉~")def call(self):print("呦吼~")c = C()  # 我来拉~
d = c
del c
d.call()  # 呦吼~
del d  # 我走拉~

对象的重生

对象的重生 不推荐使用了解原理就行

对象被del删除前将self送出去对象就算重生了

全局变量重生

class C():def __init__(self, name):self.name = nameprint("我来拉~")def __del__(self):print("我走拉~")global xx = selfc = C("python")  # 我来拉~
print(c)  # <__main__.C object at 0x000002E69F331FD0>
print(c.name)  # python
del c  # 我走拉~
print(x)  # <__main__.C object at 0x000002E69F331FD0>
print(x.name)  # python

闭包重生

class C():def __init__(self, name, func):self.name = nameself.func = funcprint("我来拉~")def __del__(self):print("我走拉~")self.func(self)def outter():x = ""def inner(y=None):nonlocal xif y:x = yelse:return xreturn innerf = outter()c = C("python", f)  # 我来拉~
print(c)  # <__main__.C object at 0x000002AB9E0E3F70>
print(c.name)  # python
del c  # 我走拉~
print(f())  # <__main__.C object at 0x000002AB9E0E3F70>
print(f().name)  # python

属性相关的魔法方法(hasattr()、getattr()、setattr()、delattr())

四个方法的基本使用

class Fish():def __init__(self, name, age):self.name = nameself.age = agef = Fish("鱼", 18)
print(hasattr(f, "name"))  # Trueprint(getattr(f, "name"))  # 鱼setattr(f, "name", "猪")
print(getattr(f, "name"))  # 猪delattr(f, "name")
print(getattr(f, "name", "1"))  # 1

getattr和hasattr 的魔法方法 __ getattribute __ 、 __ getattr __

我们在类中重写这两个方法,并且输出一下方法名字,让我们看一下如何调用的。

class Fish():def __init__(self, name, age):self.name = nameself.age = agedef __getattribute__(self, item):print("__getattribute__")return super(Fish, self).__getattribute__(item)def __getattr__(self, item):print("__getattr__")if item == "gtd":print(f"hello {item}")else:return super(Fish, self).__getattr__(item)f = Fish("鱼", 18)
print(hasattr(f, "name"))
"""
__getattribute__
True
"""print(hasattr(f, "1"))
"""
__getattribute__
__getattr__
False
"""

发现 当用户使用 hasattr 时__ getattribute __ 方法会被调用,当属性不存在时 __ getattr __ 会被调用。

getattr()呢?

...print(getattr(f, "name"))
"""
__getattribute__
鱼
"""print(getattr(f, "1"))
"""
__getattribute__
__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
"""

调用逻辑一样,但当属性不存在时会报错。

我们可以给getattr()加第三个参数,来设置默认值如:

print(getattr(f, "1", "default"))
"""
__getattribute__
__getattr__
default
"""

除了他俩会调用方法,还有self.属性也会调用这个方法.

...
f = Fish("鱼", 18)
print(f.name)
"""
__getattribute__
鱼
"""print(f.a)
"""
__getattribute__
__getattr__
AttributeError: 'super' object has no attribute '__getattr__'
"""

setattr 的魔法方法__ setattr __

我们思考一下这个方法是如何实现的?直接使用self.key = value么?让我们来试试。

class Fish():...def __setattr__(self, key, value):self.key = valuef = Fish("鱼", 18)
setattr(f, "name", "猪")  # RecursionError: maximum recursion depth exceeded

为什么会出现递归的报错呢?其实更具上面的getattr()就明白 getatr()和self.属性 其实调用的是相同的方法。

那么setattr()和self.属性 = xxx 是不是也一样呢?

...
f.name = "猪"  # RecursionError: maximum recursion depth exceeded

一样的报错,那么既然一样,递归就不难懂了。那么我们怎么避免呢?两种方法

super 避免递归
class Fish():...def __setattr__(self, key, value):super(Fish, self).__setattr__(key, value)f = Fish("鱼", 18)
f.name = "猪"
print(f.name)  # 猪
__ dict __ 避免递归
class Fish():...def __setattr__(self, key, value):self.__dict__[key] = valuef = Fish("鱼", 18)
f.name = "猪"
print(f.name)  # 猪

delattr 的魔法方法__ delattr __

delattr 和 setattr 一样 要注意避免递归问题。

class Fish():...def __delattr__(self, item):print("__delattr__")self.__dict__.pop(item)# del self.__dict__[item]f = Fish("鱼", 18)
del f.name  # __delattr__
# delattr(f, "name")    # __delattr__
print(getattr(f, "name", "没值"))  # 没值

索引、切片、迭代协议

索引和切片

索引和切片的魔法方法是 __ getitem __ 和 __ setitem __当我们对这个对象使用切片和索引操作就会调用这个方法

class Data():def __init__(self, data):self.data = datadef __getitem__(self, index):print("__getitem__", index)return self.data[index]def __setitem__(self, index, value):print("__setitem__", index, value)self.data[index] = valued = Data([1, 2, 3, 4, 5])
print(d[2])
"""
__getitem__ 2
3
"""print(d[2:5])
"""
__getitem__ slice(2, 5, None)
[3, 4, 5]
"""d[2] = 1  # __setitem__ 2 1
print(d[2])
"""
__setitem__ 2 1
__getitem__ 2
1
"""d[0:3] = d[3:5]  # __getitem__ slice(4, 5, None)    __setitem__ slice(2, 3, None) None
print(d[0:3])
"""
__getitem__ slice(3, 5, None)
__setitem__ slice(0, 3, None) [4, 5]
__getitem__ slice(0, 3, None)
[4, 5, 4]
"""

迭代协议

__ getitem __ 除了切片操作外其实还拦截了循环

...d = Data([1, 2, 3, 4, 5])for a in d:print(a)
"""
__getitem__ 0
1
__getitem__ 1
2
__getitem__ 2
1
__getitem__ 3
4
__getitem__ 4
5
__getitem__ 5
"""

虽然可以拦截,但这是一种求全的方法,正常我们应该使用 __ iter ____ next __

对象所属的类中拥有 __ iter __ 时他就是一个可迭代对象

当拥有 __ next __ 时他就是一个迭代器

__ iter __ 会返回一个迭代器,我们试试

x = [1, 2, 3, 4, 5]
# 列表不是一个迭代器
# next(x)  # TypeError: 'list' object is not an iteratoriterator_x = iter(x)while True:try:print(next(iterator_x))except StopIteration:break

已知 iter 可以返回一个迭代器,那让我们试着写一个可迭代的类吧

class Data():def __init__(self, start, stop):self.value = start - 1self.stop = stopdef __iter__(self):print("__iter__")return selfdef __next__(self):print("__next__")if self.value == self.stop:raise StopIterationself.value += 1return self.valued = Data(1, 5)
for i in d:print(i)"""
__iter__
__next__
1
__next__
2
__next__
3
__next__
4
__next__
5
__next__
"""

那如果 getitem 和 iter next 同时存在 会走谁呢?会优先走 iter 和 next 如果没有这两个方法再寻找 getitem 这种行为叫做代偿

class Data():def __init__(self, start, stop, data):self.value = start - 1self.stop = stopself.data = datadef __iter__(self):print("__iter__")return selfdef __next__(self):print("__next__")if self.value == self.stop:raise StopIterationself.value += 1return self.valuedef __getitem__(self, index):print("__getitem__", index)return self.data[index]d = Data(1, 5, [1, 2, 3, 4, 5])
for i in d:print(i)
"""
__iter__
__next__
1
__next__
2
__next__
3
__next__
4
__next__
5
__next__
"""

in 和 not in 的魔法方法__ contains __

class Data():def __init__(self, data):self.data = datadef __contains__(self, item):print("__contains__")return item in self.datadef __iter__(self):self.i = 0return selfdef __next__(self):if self.i == len(self.data):raise StopIterationitem = self.data[self.i]self.i += 1return itemdef __getitem__(self, index):print("__getitem__")return self.data[index]d = Data([1, 2, 3, 4, 5])
print(2 in d)
"""
__contains__
True
"""

如果程序中没有 contains 那么会寻找 iter next 如果还没有 使用 getitem。

比较运算符

  • < __ lt __(self,other)
  • <= __ le __(self,other)
  • > __ gt __(self,other)
  • >= __ ge __(self,other)
  • == __ eq __(self,other)
  • != __ ne __(self,other)

举个例子 我觉得字符串比较时应该是去比较字符的长度这样更有意义。

class Data(str):def __gt__(self, other):return len(self) > len(other)def __ge__(self, other):return len(self) >= len(other)def __lt__(self, other):return len(self) < len(other)def __le__(self, other):return len(self) <= len(other)def __eq__(self, other):return len(self) == len(other)def __ne__(self, other):return len(self) != len(other)d = Data("123")
s = Data("789")
print(d > s)  # False
print(d >= s)  # True
print(d < s)  # False
print(d <= s)  # True
print(d == s)  # True
print(d != s)  # False

property()和描述符

property

通过这个装饰器我们可以不用加()实现直接调用

class Data(str):@propertydef get_data(self):return "123"d = Data()
print(d.get_data)

我们还可以这样用,使用property去管理我们的get、set、del三个方法

class D():def __init__(self):self._x = 250def getx(self):return self._xdef setx(self, value):self._x = valuedef delx(self):del self._xx = property(getx, setx, delx)d = D()
print(d.x)
d.x = 2
print(d.x)
del d.x
print(d.__dict__)

描述符

如果一个类中有 __ set __ 、 __ get __ 、 __ delete __ 那么这个类被叫做描述符,用来管理别人的属性。

class A():def __get__(self, instance, owner):print("__get__", instance, owner)def __set__(self, instance, value):print("__set__", instance, value)def __delete__(self, instance):print("__delete__", instance)class B():x = A()b = B()
print(b.x)  # __get__ <__main__.B object at 0x107bc0fa0> <class '__main__.B'>
b.x = 1  # __set__ <__main__.B object at 0x107bc0fa0> 1
del b.x  # __delete__ <__main__.B object at 0x107bc0fa0>

由代码返回值可知,instance 是被描述属性类的实例,owner 是被描述属性的类

由此我们可以实现,property的功能

class A():def __get__(self, instance, owner):print("__get__", instance, owner)return instance._xdef __set__(self, instance, value):print("__set__", instance, value)instance._x = valuedef __delete__(self, instance):print("__delete__", instance)del instance._xclass B():def __init__(self):self._x = 250x = A()b = B()
print(b.x)  # 250
b.x = 1
print(b.x)  # 1
del b.x
print(b.__dict__)  # {}

这样虽然实现了类似功能,但是在一个类中调用别的类中的实例是不是有点不太优雅,我们可以这样改

class MyProperty():def __init__(self, fget, fset, fdel):self.fget = fgetself.fset = fsetself.fdel = fdeldef __get__(self, instance, owner):print("__get__", instance, owner)return self.fget(instance)def __set__(self, instance, value):print("__set__", instance, value)self.fset(instance, value)def __delete__(self, instance):print("__delete__", instance)self.fdel(instance)class D():def __init__(self):self._x = 250def getx(self):return self._xdef setx(self, value):self._x = valuedef delx(self):del self._xx = MyProperty(getx, setx, delx)d = D()
print(d.x)
d.x = 1
print(d.x)
del d.x
print(d.__dict__)

类方法和静态方法

类方法

通过类方法装饰器 @classmethod 可以定义一个类方法,第一个参数是cls表示当前的类。

我们可以通过这个来管理我们的对象实例,如下:

class Data():count = 0def __init__(self):Data.count += 1@classmethoddef get_count(cls):return Data.countd1 = Data()
d2 = Data()
d3 = Data()print(Data.get_count())  # 3

静态方法

我们可以通过 @staticmethod 装饰器实现一个静态方法,也同样可以实现上述功能。

class Data():count = 0def __init__(self):Data.count += 1@staticmethoddef get_count():return Data.countd1 = Data()
d2 = Data()
d3 = Data()print(Data.get_count())

对比

既然都能实现,那到底那种方法好一些呢?

两种方法都是类层级的方法,无需创建实例去使用。区别是类方法需要和类绑定,静态方法则不需要。

如果和类发生关系,则需要使用类方法,否则使用静态方法即可。

如果发生继承关系,我们想让每个类都能统计自己的实例数量,通过类方法就可以实现继承。静态适合在子方法中使用,或者某些全局情况下使用

class Data():count = 0@classmethoddef add_count(cls):cls.count += 1Data.count += 1def __init__(self):self.add_count()@classmethoddef get_count(cls):return cls.count@staticmethoddef get_all_count():return Data.countclass A(Data):count = 0class B(Data):count = 0class C(Data):count = 0A1 = A()
B1, B2 = B(), B()
C1, C2, C3 = C(), C(), C()print(A.get_count())  # 1
print(B.get_count())  # 2
print(C.get_count())  # 3# 静态方法实现
print(Data.count)  # 6

Python(13)面向对象相关推荐

  1. Python之面向对象进阶

    Python之面向对象进阶 进阶有:Python 类的成员.成员修饰符.类的特殊成员. 一.类的成员 类的成员可以分为三大类:字段.方法和属性. 注:所有成员中,只有普通字段的内容保存对象中,即:根据 ...

  2. Python之面向对象继承和派生

    Python之面向对象继承和派生 什么是继承: 继承是一种创建新的类的方法.在Python中,新建的类可以继承自一个或多个父类.原始类称为基类或超类. 新建的类称为派生类或子类. Python中类的继 ...

  3. python 完全面向对象_python面向对象实战

    一 面向对象的程序设计的由来 二 什么是面向对象的程序设计及为什么要有它 面向过程的程序设计:核心是过程二字,过程指的是解决问题的步骤,即先干什么再干什么......面向过程的设计就好比精心设计好一条 ...

  4. 16.1、python初识面向对象(1)

    初识面向对象 楔子 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战>的游戏,你就思考呀,人狗作战,那至少需要2个角色,一个是人, 一个是狗,且人和狗都有不同的技能,比如人 ...

  5. Python初识面向对象

    一.Python初识面向对象 1.1 封装 class Person:country='中国' #静态字段,可以直接调用def __init__(self,name,pwd): #Python类自带的 ...

  6. Python之面向对象类和对象

    Python之面向对象类和对象 定义一个类:class 定义类的语法: class Test(object):"""类里定义一类事物共同的技能.可以是变量,也可是函数.& ...

  7. [.net 面向对象编程基础] (13) 面向对象三大特性——多态

    [.net 面向对象编程基础] (13) 面向对象三大特性--多态 前面两节,我们了解了面向对象的的封装和继承特性,面向对象还有一大特性就是多态.比起前面的封装和继承,多态这个概念不是那么好理解.我们 ...

  8. python 内存溢出能捕获吗_从0基础学习Python (19)[面向对象开发过程中的异常(捕获异常~相关)]...

    从0基础学习Python (Day19) 面向对象开发过程中的=>异常 什么是异常 ​ 当程序在运行过程中出现的一些错误,或者语法逻辑出现问题,解释器此时无法继续正常执行了,反而出现了一些错误的 ...

  9. python基础——面向对象的程序设计

    python基础--面向对象的程序设计 1 什么是面向对象的程序设计 面向过程的程序设计的核心是过程,过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西. 优 ...

  10. python是面向对象还是面向过程的语言_关于python是面向对象还是面向过程的分析...

    关于python是面向对象还是面向过程的分析 发布时间:2020-04-07 16:10:55 来源:亿速云 阅读:24 作者:小新 今天小编给大家分享的是关于python是面向对象还是面向过程的分析 ...

最新文章

  1. 倒计时1天,2018 AI开发者报名通道即将关闭(附参会提醒)
  2. 深度学习最近发现详细分析报告
  3. windows下vs2013使用C++访问redis
  4. dao传递类参数 mybatis_MyBatis DAO层传递参数到mapping.xml 几种方式
  5. SQLPLUS命令使用大全
  6. java测试开发_测试开发系类之Java常用知识点
  7. 【JavsScript】webapp的优化整理
  8. c语言课后题答案83,C语言练习试题和答案
  9. 2005-2020POI数据 高德POI数据 高德信息点数据 百度POI数据分析下载
  10. MySQL Workbench 6.3CE 汉化及使用教程(转载)
  11. java uml类图虚线实线_时序图的实线和虚线-类图中的实线与虚线-用例图中实线箭头表示什么...
  12. 关于switchcase和ifelse的效率对比分析
  13. 使用RTMP协议实现视频桌面共享功能
  14. 等额本息与等额本金,从财务管理的角度充分考虑货币时间价值,哪个对于购房人来说更划算?
  15. 计算机用户分为哪4类,计算机的分类-通常将计算机分为哪几类?通常将计算机分为哪几类,各自的特点和用途 爱问知识人...
  16. 电子商务企业如何把握大数据?
  17. window onload
  18. 数据库MySQL(一) 常用查询语句
  19. 我爷爷吸烟,我爸爸也吸烟,轮到我不能断了香火
  20. 封面故事:十大创业困境

热门文章

  1. python 解小学数学题_python 解数学题!求帮助
  2. oracle动态 returning,ORACLE RETURNING 用法总结
  3. 吉林大学计算机学院推免名额,吕帅-吉林大学计算机科学与技术学院
  4. MongoDB 报错:数据库连接失败 MongooseServerSelectionError:connect ECONNREFUSED 127.0.0.1:27017
  5. 例题总结——算24点
  6. jQuery个人总结
  7. 数据分析之【渐近显著性(双侧)】与【精确显著性[2*(单侧显著性)]】的异同
  8. python time strptime_Python time strptime()方法 时间操作
  9. 可解释人工智能导论-读书笔记(2)
  10. 嵌入式 Linux 2.6.31内核优化指南小结