Python中提供了两种方法装饰器,@classmethod和@staticmethod。classmethod被称为“类方法”,而staticmethod被称为“静态方法”。被这两个装饰器装饰的方法,不需要对实例进行初始化就可以直接调用:
class Demo:
@classmethod
def Klassmeth(*args):
pass
@staticmethod
def statmeth(*args):
pass
Demo.klassmeth()
Demo.statmeth()
classmethod通常是用于定于备选构造方法。对于Python来说,虽然经常把__init__方法认为是构造方法,但真正创建对象的方法是__new__,对象创建完成以后会把参数传递给__init__方法。当一个__init__方法不够用的时候(即想要通过一个类完成多个不同实例的创建),往往会使用到classmethod,比如:
class Draw:
def __init__(self, x, y):
self.x = x
self.y = y
@classmethod
def draw(cls, position):
return cls(*position)
draw = Draw(x, y)
draw2 = Draw.draw((x, y))
这样,无论传入的参数是x和y的各自的数值还是一个包含坐标值的tuple,Draw都可以创建相应的实例。在Java中似乎并不需要这么做,因为Java可以具有多个构造方法:
public class Draw{
Draw(int x, int y){
this.x = x;
this.y = y;
}
Draw(int[] position){
this.x = position[0];
this.y = position[1];
}
public static void main{
int a = 0;
int b = 0;
int[] c = {
0, 0};
Draw draw = new Draw(a, b);
Draw draw2 = new Draw(c);
}
}
如此一来,就不需要使用classmethod去声明多个构造方法了。而Java中的“类方法”则和Python中的“staticmethod"有些许相似之处。虽然被称为”类方法“,但是其本身使用static修饰符进行修饰,被static修饰的方法不能够使用实例的方法和属性。因为Java是纯OOP语言,类中的函数常被称为“方法”,使用static修饰的方法可以看成一个普通的函数,刚好存在于某个类当中。而Python中的staticmethod因为没有传入self参数,本身也可以看作一个普通函数。
staticmethod装饰器要求传入的参数是callable的即可,也就是说,我们可以给class添加该装饰器,只要这个class实现了__call__方法:
class Demo:
a = 1
def __init__(self):
self.b = 2
@staticmethod
class Demo1:
def __init__{
self}:
pass
def printf(self):
print(a) #可以顺利执行
print(self.b) #无法执行
demo1 = Demo.Demo1()
demo1.printf()
由于内部类没有传入外部类的self参数,所以内部类是无法使用外部类的实例属性的,除非在外部类中将内部类实例化。但是内部类可以使用外部类的类属性,这和Java中的静态内部类比较相似:
public class Outer{
static int a = 1;
int b = 2;
static class inner{
void printf{
System.out.println(a); //可以使用
System.out.println(b); //不可以使用
}
}
}
Java中的main方法是放在类中的,每一个类都是一个.Java文件,在测试代码的时候如果在每一个文件下都加上一个main方法就会使原本就臃肿的Java代码显得更加累赘,这时候就可以把测试代码放在静态内部类中。
Java里面还有一种内部类是匿名内部类。这个很好理解,匿名类就是没有签名的类,类在实例化的时候去实现。这看起来和JavaScript的风格很像,JavaScript常用于浏览器和桌面软件这样具有大量交互式场景的方面,因此会使用大量的回调函数,在调用的时候才去实现具体的细节。而Java中,这样的应用场景多见于安卓开发中,因为安卓也存在大量的交互式行为,常常需要监听事件。
以下是匿名内部类的一个例子:
public abstract Cat{
public void sleep();
}
public class Outer{
public static void main(String[] args){
Cat cat = new Cat(){
public void sleep() throws Exception{
Thread.sleep(1000);
}
}
}
}
有的时候,我们会在使用一些API的时候重写它下面的方法,进而改变这些接口的行为方式,这时候它们也可以看作是匿名的。假设我们有两个小孩的实例,他们都有年龄这个属性,那么可以通过重写Comparator接口下的compare方法来用比较年龄的方式来比较这两个实例:
List<Children> children = new ArrayList<>();
children.add(new Children(10));
children.add(new Children(12));
Children olderChild = Collections.max(children, new Comparator<Books>(){
@Override
public int compare(Children c1, Children c2){
if (c1.getAge() - c2.getAge() > 0){
return 1;
}
else if(c1.getAge() - c2.getAge() == 0){
return 0;
}
else {
return -1;}
}
});
这里,调用max方法将会输出年龄最大的小孩的实例。这么写的话,和JS里的排序写法看起来就比较相似了。但是在python中不需要这么做,只要用一个参数key就搞定了:
import random
import math
class Children:
__slots__ = ["age", "name"]
def __init__(self, age, name):
self.age = math.ceil(age)
self.name = name
def __repr__(self):
return "name: {} age: {}".format(self.name, self.age)
childrens = list()
name = "".join([chr(random.choice(range(97, 113))) for _ in range(5)])
for _ in range(100):
childrens.append(Children(random.random * 10, name))
childrens.sort(key = lambda x: x.age)
在里初始化了100个小孩的实例,并且把他们的年龄设置在1和11之间,然后将其放入一个list中。我们调用了list的sort方法,并使用其自带的key参数,传入一个匿名的lambda函数将每一个实例的年龄属性作为排序的依据,就轻松完成了对100个小孩的实例的排序。在PySpark里面我们可以经常看到利用这种API去处理大量的数据——尽管这样的写法不太利于阅读。
不过在Java8中也新引入了lambda表达式,使用它可以代替大部分的匿名内部类,从而让代码写起来更加精简。于是,在Java8中我们也可以这样去写比较代码:
List<Children> children = new ArrayList<>();
children.add(new Children(10));
children.add(new Children(12));
Children olderchild = Collections.max(children, (first, second)->{
return first.getAge() - second.getAge();
});
这样同样可以找出年龄最大的小孩,并且还有一个好处是:在重写compare方法时由于其返回值是int类型,因此当我们比较Double类型的值时,还需要调用Double.compare()方法,而如果使用匿名函数的话,一个简单的返回值就可以搞定了。