基础
像init函数,开头和末尾各有两个下划线,这是一种约定,表示Python的默认函数,旨在避免Python默认方法与普通方法发生名称冲突。
类名应该采用驼峰命名法,即将类名中的每个单词的首字母都大写,而不使用下划线,实例名和模块名都采用小写格式,并在单词之间加上下划线。
在import时,先导入系统的库,然后空一行,再导入自己写的模块,这样更清晰。
# object表示从object类继承,相当于Java中extends后的类名
# object是否可省略与python版本有关,Python2.7需要,Python3可以不要
# 为了兼容,建议还是不要省了
class Student(object):
age = 50; # 类变量,类似于Java中的static变量
def __init__(self, name, score): # self 是一个指向实例的引用,类似于Java中的this
# __init__ 是构造函数,每次初始化实例时,都会执行
self.__name = name # python中对成员变量的引用需要使用self明确指出
self.score = score # 因为这是跟实例有关的,所以需要self.
def print_score(self):
print('%s: %s' % (self.__name, self.score))
# 可以创建多个实例 st1 = Student("zhangsan", 20) st2 = Student("lisi", 22)
>>> bart = Student('Bart Simpson', 59) # 创建一个实例
>>> bart.print_score()
Bart Simpson: 59
>>> bart.score = 60 # 可以通过实例名.成员变量来直接修改实例的成员变量值
# 但__name变量就不行,因为在变量前加上__,表示这是私有变量,只能在类内部访问
# 此时如果依然用bart.__name="james",并没有改变原来的成员变量,
# 只是生成了一个新变量,因为__name被改名成了_Student__name,
# 所以,实际上依然是可以在外部访问的,但是建议不要这么做。
# 还有__name__这样的,不是私有变量,而是特殊变量
# 还有_name,表示我不希望被外部引用,但是实际上就是普通成员变量
继承
普通继承
class Car(object):
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
long_name = str(self.year) + ' ' + self.make + ' ' + self.model
return long_name.title() # title()是首字母大写
def fill_gas_tank(self):
print("now fill car gas tank")
class ElectricCar(Car):
def __init__(self, make, model, year, battery_size):
# Python2.7里super()的参数不能省
super(ElectricCar, self).__init__(make, model, year)
self.battery_size = battery_size
def desc_battery(self):
print("this car bettery size is :", self.battery_size)
def fill_gas_tank(self): # 重写父类方法
print("electric car doesn't need a gas tank!")
my_car = Car('benz', 'outback', 2013)
print(my_car.get_descriptive_name())
my_tesla = ElectricCar('tesla', 'model_s', 2016, 70)
print(my_tesla.get_descriptive_name())
my_tesla.fill_gas_tank()
# 2013 Benz Outback
# 2016 Tesla Model_S
# electric car doesn't need a gas tank!
继承时的参数传递
当没有明确写出构造函数init时,系统会默认传参给父类。
import collections
class Student1(collections.namedtuple('Person1', ["name", "age"])):
def print(self):
print("this is print function")
s1 = Student1("wangwu", 30)
print("s1 =", s1)
----------------------------------------------------------------------------
class Person2():
def __init__(self, name, age):
print("this is Person2 init func")
print("name =", name)
print("age = ", age)
class Student2(Person2):
pass
s2 = Student2("sunliu", 40)
print("s2 =", s2)
----------------------------------------------------------------------------
class Person3():
pass
class Student3(Person3, collections.namedtuple('Person1', ["name", "age"])):
def print(self):
print("this is print function")
print("name =", self.name)
s3 = Student3("zhaoqi", 70)
print("s3 =", s3)
s3.print()
----------------------------------------------------------------------------
# res
s1 = Student1(name='wangwu', age=30)
this is Person2 init func
name = sunliu
age = 40
s2 = <__main__.Student2 object at 0x7ffdacaf0668>
s3 = Student3(name='zhaoqi', age=70)
this is print function
name = zhaoqi
多继承
python中的多继承,在初始化时是按照MRO(Method Resolution Order)表来进行顺序的解析。
class Base(object):
def __init__(self):
print("enter Base")
print("leave Base")
class A(Base):
def __init__(self):
print("enter A")
super(A, self).__init__()
print("leave A")
class B(Base):
def __init__(self):
print("enter B")
super(B, self).__init__()
print("leave B")
class C(A, B):
def __init__(self):
print("enter C")
super(C, self).__init__()
print("leave C")
c = C()
print(C.mro())
---------------------------------------
# res
enter C
enter A
enter B
enter Base
leave Base
leave B
leave A
leave C
[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]
从结果可以看出按照MRO表,就是先进入A,然后A再super(A, self),
注意,此时的self其实是C对象,所以,按照C对象的顺序表,就进入了B,
B再super(B, self),此时self还是C对象,于是按照顺序表进入了Base,因此会有以下结果。
装饰器
@classmethod
classmethod 修饰符对应的函数不需要实例化,不需要 self 参数,但第一个参数需要是表示自身类的 cls 参数,可以来调用类的属性,类的方法,实例化对象等。
class A(object):
b = 2
def __init__(self, a=1):
print("this is init method,a=", a)
self.a = a
def func1(self):
print("fun1")
@classmethod
def func3(cls):
print("func3")
@classmethod
def func2(cls):
print("func2")
cls().func1() # func1是属于实例的方法,因此就需要先实例化,再调用
cls.func3() # func3是属于类的方法,可以直接用类调用
cls(5) # 实例化一个对象
print("a=", cls(6).a)
print("b=", cls.b)
A.func2()
-- output
func2
this is init method,a= 1
func1
func3
this is init method,a= 5
this is init method,a= 6
a= 6
b= 2
@staticmethod
staticmethod不需要任何参数,与classmethod不同,classmethod的主要使用场景是当你想用类来调用方法,而不是对象来调用方法时,而staticmethod一般是用于多个类之间共享该方法,就可以定义为staticmethod,不过在python中,这种场景很少。
@property
@property,把一个getter
方法变成属性,同时会创建另外一个装饰器,如下代码就是@width.setter
和@height.setter
。
从resolution可能看的更清晰一点,创建一个resolution方法,在通过装饰器以后,就默认Screen类有了一个属性,叫做resolution,所以下面可以通过s.resolution调用。并且此时resolution是一个只读属性,不能被修改,从而达到访问控制的目的。
class Screen(object):
@property
def width(self):
return self._width
@width.setter
def width(self, width):
self._width = width
@property
def height(self):
return self._height
@height.setter
def height(self, height):
self._height = height
@property
def resolution(self):
return self._width * self._height
# 测试:
s = Screen()
s.width = 1024
s.height = 768
print('resolution =', s.resolution)
if s.resolution == 786432:
print('测试通过!')
else:
print('测试失败!')
s1 = Screen(100, 700)
print('resolution =', s1.resolution)
# res
resolution = 786432
测试通过!
Traceback (most recent call last):
File "/Users/liufei/Downloads/test.py", line 30, in <module>
s1 = Screen(100, 700)
TypeError: object() takes no parameters
一些奇技淫巧
collections.namedtuple
第一次看到这个类是从tensorflow的源码中,tf.featurecolumn.numericcolumn中的_NumericColumn类继承自collections.namedtuple,这样_NumericColumn可以在没有__init方法的情况下,直接使用self.key等属性:
class _NumericColumn(_DenseColumn,
collections.namedtuple('_NumericColumn', [
'key', 'shape', 'default_value', 'dtype',
'normalizer_fn'
])):
"""see `numeric_column`."""
@property
def name(self):
return self.key
collections.namedtuple
是一个工厂方法,它可以动态的创建一个继承tuple
的子类。跟tuple
相比,返回的子类可以使用名称来访问元素。最大的好处应该是可以提高代码的可读性,最常见的是作为父类给子类提供属性,可以减少很多代码,并且子类的返回结果也更清晰易懂,可以参考上面#继承时的参数传递部分,可以看到,如果是从namedtuple继承,子类返回结果直接可以看到类名,属性值等,而从普通类继承的,则只返回子类的地址。
import collections
Student = collections.namedtuple('Teacher', ['name', 'age'])
s = Student('zhangsan', 25)
print(s)
print("name =", s.name)
t = Teacher("lisi", 26)
# res
Teacher(name='zhangsan', age=25)
name = zhangsan
Traceback (most recent call last):
File "/Users/liufei/Downloads/test.py", line 13, in <module>
t = Teacher("lisi", 26)
NameError: name 'Teacher' is not defined
理解dict
dict用来存储类和对象的属性和方法,以及其他的一些信息。
类的dict存储自己类层面的信息,如类的变量,函数等,不存储函数内的信息,同时不包括父类的信息,对象的dict只存储自己独有的信息。
# -*- coding: utf-8 -*-
class A(object):
"""
This is doc.
"""
a = 0
b = 1
def __init__(self):
self.a = 2
def test(self):
print 'A func.'
@staticmethod
def static_test(self):
print 'A static func.'
@classmethod
def class_test(self):
print 'A calss func.'
obj = A()
print("A.__dict__:", A.__dict__)
print("\n")
print("obj.__dict__:", obj.__dict__)
class B(A):
def __init__(self, c=3):
super(A, self).__init__()
self.c = c
def test(self):
print 'B func.'
print("B.__dict__:", B.__dict__)
print("ojb_B.__dict:", B().__dict__)
--output
('A.__dict__:', dict_proxy({'a': 0, '__module__': '__main__', 'b': 1,
'class_test': <classmethod object at 0x10ade7210>,
'__dict__': <attribute '__dict__' of 'A' objects>,
'__init__': <function __init__ at 0x10ad70050>,
'test': <function test at 0x10ad702a8>,
'__weakref__': <attribute '__weakref__' of 'A' objects>,
'__doc__': '\n This is doc.\n ',
'static_test': <staticmethod object at 0x10ade71d8>}))
('obj.__dict__:', {'a': 2})
('B.__dict__:', dict_proxy({'test': <function test at 0x10ae670c8>,
'__module__': '__main__',
'__doc__': None,
'__init__': <function __init__ at 0x10ae67050>}))
('ojb_B.__dict:', {'c': 3})