前言:
python中所有的数据都是以对象的形式存在,无论是简单的数字类型还是复杂的代码模块。然而python特殊的语法形式巧妙的将实现对象机制的大量细节隐藏了,比如输入num = 7
就可以创建一个值为7的整数对象,并且将这个对象赋值给变量num。只是这个对象包含了加法,乘法之类方法的对象。
抽象可以节省很多工作,实际上它的作用还要更大,它是使得计算机程序可以让人读懂的关键。计算机非常乐于处理精准和具体的指令,但是人就不一样,人需要简洁但不需要具体和精准
当你需要许多相似的行为,但是不同的状态时,使用对象是最好的选择
函数
- 创建函数
函数是可以调用,它执行某种行为并且返回一个值。一般来说,内建的callable函数可以用来判断函数是否可调用。callable( f )
- 记录函数
给函数写文档,加入注释或直接在def后面写上字符串 - 位置参数:定义函数的时候括号里面的形参,位置很重要,比名字重要
- 关键字参数:使用参数名提供的参数叫做关键字参数
hello(greeting = "Hello",name = "world")
,参数名和值一定要对应,关键字参数可以在函数中给参数提供默认值,提供默认值之后,在调用的时候就可以提供可以不提供了。 - 收集位置参数(不限量):有时候让用户提供任意数量的参数,在形参之前加*,结果将以元组的形式传入进函数,==如果与其他的位置参数联用,则星号自动收集其余位置的参数。可以以元组的形式传入形参位置。
- 收集关键字参数(不限量):在函数定义式的括号里参数前加两个星号**,然后就可以将不限数量的关键字参数以字典的形式传入函数。可以以字典的形式传入形参位置。
对象
对象:基本上可以看做数据(特性)以及由一系列可以存取,操作这些数据的方法所组成的集合(即attribute和function),使用对象代替全局变量和函数的原因有很多,最主要的原因:
- 多态:可以对不同类的对象使用同样的操作。任何不知道对象是什么类型,但是又要对对象“做点什么”的时候,都会用到多态。可以让用户对于不知道是什么类的对象进行方法调用
- 封装:对外部世界隐藏对象的工作细节:封装是可以不用关心对象是如何构建的而直接使用。
- 继承:以普通的类为基础建立专门的类对象:程序员不想把同一段代码写好几次。比如已有类,但是又想建立一个非常类似的类,新的类可能只是添加几个方法。在编写新类时,又不想把旧的代码全部复制过去。
__init__和self
_init_()是python中一个特殊的函数名,用于根据类的定义创建实例对象。当你声明__init__( )方法时,第一个参数必须为self。当实例化一个对象时,在内存中实例化对象之后,会自动调用__init__()方法,将新建的对象作为self传入。
self参数指向了这个正在被创建的对象本身。
对象的创建
有两个步骤:首先构造对象,其次,初始化对象。
__new__构造对象
__init__初始化对象
基类(超类)
命名规则:Python编程社区有一个普遍认可的约定,函数用小写字母命名(另外有下划线进行强调),而类名各个单词的首字母大写。
python中的继承为超类,对个继承,就是多个超类。
- 查看是否为子类:issubclass()
- 查看基类:bases( )
覆盖方法(override)
方法中没有共有私有的定义
新建的子类会自动继承父类的所有信息,如果子类重新定义一个与父类同名的方法时,子类的方法就会将父类的方法覆盖掉.例如:下面的代码就会将父类的exclaim方法覆盖掉,init()同理。
但是注意一点:python不支持重载(overload)
class Car():
def exclaim(self):
print("im a car")
class YuGo(Car):
def exclaim(self):
print("dhnwdhjkewh")
增加属性,add attribute and method
变量中没有public和private的性质
属性分为两类:
- 类属性:所有的实例共享的属性变量
在累中定义好的变量属性,有可能在__init()方法中初始化的,有可能没有初始化的,但是对于所有的新生成的实例,他们的类属性变量都是相同的。 - 实例对象属性:
如果某个实例在后续的使用中需要添加新的属性变量,那就可以创建实例属性变量。在python的机制中,变量是不需要定义就可以直接使用的,所以其属性也是不需要定义的,可以直接使用。
在添加属性的时候,如果一个方法x新增了一个属性A1,然后,在另外的一个方法y调用A1,如果先调用了x,再调用y,程序不会报错。但是当没有调用x,而先调用y,则会报错说属性不存在,只要一个实例,执行了一条语句,像下面的p1.x= 5时,他就相当于在p1中新增了一个属性(attribute),在后续的过程中就可以在p1中使用这个属性。
class Point:
pass
p1 = Point()
p2 = Point()
p1.x = 5
p1.y = 4
p2.x = 3
p2.y = 6
print(p1.x, p1.y)
print(p2.x, p2.y)
If we run this code, the two print statements at the end tell us the new attribute
values on the two objects
如上述代码,我们给两个新生成的对象,添加了两个新的属性,但是它不会,改变Point()的定义属性,当新生成一个对象c的时候,c是没有c.x,c.y的。
super从父类得到帮助
作用:
- 解决多继承(钻石问题)的歧义问题。
- 代替父类名,方便后期维护
- 调用父类方法可以不传参数self
上面说的是覆盖父类的方法,如果想要调用父类的方法怎么办?
class Person():
def __init__(self,name):
self.name = name
class EmainPerson(Person):
def __init__(self,name,email):
super().__init__(name)
self.email = email
如上,在子类定义__init__()方法时,父类的__init__()方法会被覆盖。因此,在子类中,父类的初始化方法并不会自动调用,我们必须显示调用它
- 通过super()方法获取父类Person的定义
- 子类的__init__()调用了Person.init()方法。它会自动将self参数传递给父类。因此,你只需要传递其他参数就行,上面的例子其他参数是name
- self.email = email这行新的代码真正起到作用将EmailPerson与Person分开
我们可以在定义EmainPerson的时候,重新定义__init__()方法如下:
class EmainPerson(Person):
def __init__(self,name,email):
self.name = name
self.email = email
但是这样做有悖于使用继承的初衷,我们应该让Person完成它自己应该完成的事情,优点:当Person类的定义改变时,使用super可以保证这些改变自动修改,不用手动修改。
self自辩
C++和java在类的定义中是已经假定了有了实例的。而python没有假定,所以只有用self来代表实例对象。
self必须为类方法的第一个参数。python使用self参数找到正确的对象所包含的特性和方法。
例如:
class Car():
def exclaim(self):
print("im a car")
car1 = Car()
car1.exclaim()
car1.exclaim()---->Car.exclaim( car1 )
上述代码做了哪些事情?
- 查找car对象所属的类(Car)
- 把car对象作为self参数传递给Car类所包含的exclaim()方法。
例子:
>>> class Point:
... def reset():
... pass
...
>>> p = Point()
>>> p.reset()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: reset() takes no arguments (1 given)
定义属性(property)的方法:修饰符
attribute译为特性(下面的name为特性),就是我们常说的对象特性,property译为属性
使用属性对特性进行访问和设置
class Duck():
name= none
def __init(self):
....
pass
def get_name(self):
....
pass
def set_name(self):
....
pass
name = property(get_name,set_name)
修饰符:定义属性的方法,
@property,用于指示getter()方法
@name.setter,用于指示setter()方法