python function - 파이썬 함수

in kr •  6 years ago 

python-logo.png

3. Function

  • 함수의 구조와 종류, 사용방법등에 대해서 학습합니다. 함수는 반복되는 코드를 묶음으로 효율적인 코드작성이 가능하도록 해주는 기능입니다.

3.1 Basic Function - 기본함수

함수는 Definition의 약자인 def 예약어를 사용하여 선언 합니다. def 예약어를 작성후 한칸 띄고 앞에서 배운 식별자 규칙에 따라서 함수 이름을 작성합니다. 함수 이름 작성후 바로 뒤에 파라미터를 넣어줄 () 특수문자를 써주고 () 안에는 함수에서 사용할 파라미터 이름을 작성합니다. 그리고 함수 선언을 하는 라인의 마지막에는 : 문자를 붙임으로 함수 선언의 마지막을 나타냅니다. 함수가 호출되면 함수 안에 있는 코드가 실행 됩니다.

선언

def <function_name>():
    code

호출

<function_name>()
# 함수의 선언
def data_science():
    print("dss plus 1")
# 함수의 호출
data_science()

결과

dss plus 1

3.2 Parameter & Argument - 파라미터와 아규먼트

3.2.1 Parameter

파라미터는 함수를 호출할때 전달 하는 데이터를 받아서 함수 내에서 사용되는 변수 명입니다.

def <function_name>(<parameter_name_1>,<parameter_name_2>):
    code
def data_science(string):
    print(string)

3.2.2 Argument

아규먼트는 함수를 호출할때 데이터를 전달하는 것으로 아래와 같이 사용합니다.

<function_name>(<argument_1>, <argument_2>):
arg = "dss plus 1"
data_science(arg)

결과

dss plus 1

3.2.3 Default Parameter

함수의 파라미터에 아래와 같이 디폴트값을 설정해 줄수 있습니다. 디폴트값이 설정된 파라미터 변수는 함수를 호출할때 해당 파라미터에 대한 아규먼트가 없으면 디폴트로 설정된 값이 파라미터 변수로 들어가게 됩니다.

def <function_name>(<key_name_1>=<value_1>,  <key_name_2>=<value_2>):
    code

아래의 코드는 minus 함수를 호출할때 아규먼트 5만 설정이 되어 함수의 num1 파라미터에는 아규먼트 값이 5가 num2 파라미터에는 디폴트 값이 2가 들어갑니다.

def minus(num1 = 3, num2 = 2):
    print(num1 - num2)

minus(2)

결과

0

디폴트 파라미터를 사용할때는 디폴트로 설정된 파라미터가 항상 뒤쪽에 사용해야 합니다. 그러므로 아래의 코드는 에러가 발생됩니다.

def minus(num1 = 3, num2 = 2, num3):
    print(num1 - num2 - num3)

minus(5)

위의 코드는 아래와 같이 사용해야 에러 없이 사용이 가능 합니다.

def minus(num3, num1 = 3, num2 = 2):
    print(num1 - num2 - num3)

minus(5)
minus(5, 1)
minus(5, 1, 4)

결과

-4
-6
-8

3.2.4 keyword Argument

키워드 아규먼트를 사용하는것은 아래와 같이 사용합니다. 아규먼트가 들어가는 부분에 (key=value) 값이 들거 갑니다. 아규먼트의 키값고 함수 파라미터의 변수명이 같은 변수에 value 값이 들어가게 됩니다.

def <function_name>(<key_1>, <key_2>):
    code
<function_name>(<key_1>=<value_1>, <key_2>=<value_2>)

아래의 코드는 num1과 num2를 파라미터로 받아 num1에서 num2를 뺀 결과를 출력하는 함수를 선언하였습니다. 함수를 호출할때 keyword argument를 사용하여 순서에 의해서 함수의 parameter에 대입되는것이 아니고 parameter의 변수명과 함수 호출시 설정한 argument의 keyword에 의해서 대입됩니다.

def minus(num1, num2):
    print(num1 - num2)

minus(2, 3)    
minus(num2 = 3, num1 = 2)

결과

-1
-1

3.3 Return - 리턴

리턴은 함수를 호출했을때 결과 데이터를 반환하는 용도로 사용됩니다. 함수에는 리턴이 있는 함수가 있고 리턴이 없는 함수가 있습니다.

아래의 코드는 plus이라는 두개의 파라미터를 받아 더한 결과를 리턴하는 함수와 1과 2라는 숫자 값을 아규먼트로 사용하여 plus 함수를 호출한후 함수의 리턴값(결과값)을 result라는 이름의 변수에 저장한후 result 변수를 출력하는 코드 입니다.

def plus(a, b):
    return a + b

result = plus(1, 2)

print(result)

결과

3
def echo(a, b):
    return a, b

result1, result2 = echo(1, 2)

print(result1, result2)

결과

1 2
def dss(number):
    if number > 10:
        return
    print("dss")

dss(11)
dss(8)

결과

dss

3.4 *args, **kwargs - 아규먼트들과 키워드 아규먼트들

*args, **kwargs는 함수를 호출할때 보내는 아규먼트의 갯수를 특정할수 없을 때, 함수의 파라미터를 넣는 영역에 사용됩니다. *args, **kwargs를 사용하면 함수 호출시 파라미터로 전달하는 아규먼트의 갯수에 상관 없이 파라미터를 받아올수 있습니다.

3.4.1 *args

함수에서 여러개의 파라미터를 한꺼번에 받아올때 사용합니다. 파라미터로 받는 데이터는 tuple 데이터 타입으로 받습니다. *은 코드에서 all(전부)를 의미하기 때문에 *args는 모든 아규먼트를 받아온다는 의미로 사용합니다.

def print_args(*args):
    print(args)
    print(type(args))
    print(args[4])
    print(args[5][1])

print_args(1, 2, 3, "fast", "campus", ["data", "science"])

결과

(1, 2, 3, 'fast', 'campus', ['data', 'science'])
<class 'tuple'>
campus
science

아래의 코드는 *args를 이용하여 평균을 구하는 코드 입니다. 이와같이 *args는 아규먼트로 몇개의 데이터가 설정될지 모를때 사용하면 좋습니다

def avg_func(*args):
    return sum(args) / len(args)

a = avg_func(100, 70, 80, 99, 85, 60, 80)

print("avg : {}".format(round(a, 2)))

결과

avg : 82.0

iterable 데이터 앞에 *을 붙여 데이터를 묶어서 아규먼트로 사용하여 함수를 호출할수도 있습니다. 호출하는하는 함수가 *args를 파라미터를 사용해야 합니다.

def avg_func(*args):
    print(args)
    return sum(args) / len(args)

ls = [100, 70, 80, 99, 85, 65, 80]

# a = avg_func([100, 70, 80, 99, 85, 65, 80])
# a = avg_func(ls)

# a = avg_func(100, 70, 80, 99, 85, 65, 80)
a = avg_func(*ls)

print("avg : {}".format(round(a, 4)))

결과

(100, 70, 80, 99, 85, 65, 80)

3.4.2 **kwargs

앞에서 함수를 호출할때 아규먼트에 키워드를 넣을수 있다고 설명하였습니다. **kwargs는 함수에서 파라미터로 모든 키워드 아규먼트를 받습니다. **kwargs 모든 키워드와 아규먼트를 받아온다는 의미로 사용됩니다. key값이 있는 아규먼트이기 때문에 파라미터로 받는 데이터는 dictionary 데이터 타입으로 받습니다.

def avg_func(**kwargs):
    print(kwargs)
    print(type(kwargs))
    total, count = 0, 0
    for subject, point in kwargs.items():
        print(subject, point)
        total += point
        count += 1
    return total / count
a = avg_func(korean = 100, english = 70, math = 80, science = 90)
print("avg : {}".format(round(a, 2)))

결과

{'korean': 100, 'english': 70, 'math': 80, 'science': 90}
<class 'dict'>
korean 100
english 70
math 80
science 90
avg : 85.0

아래의 코드와 같이 *args, **kwargs를 함께 사용할수 있습니다.

def test_func(*args, **kwargs):
    print(args)
    print(kwargs)
test_func(1, 2, 3, "fastcampus", "datascience", korean = 100, english = 70, math = 80)

결과

(1, 2, 3, 'fastcampus', 'datascience')
{'korean': 100, 'english': 70, 'math': 80}

3.5 Docstring - 함수설명

PEP 20 : Zen of Python에 readablity counts(가독성은 중요하다)라는 문장이 있습니다. docstring은 함수를 사용할때 함수에 대한 설명을 넣는것을 의미합니다.

3.5.1 single line docstring

docstring의 위치는 아래의 코드와 같이 함수를 선언한 코드 아래에 문자열로 넣습니다.

def echo(anything):
    'echo returns its input argument'
    return anything

result1 = echo("data_science")
result1

결과

data_science
# docstring 확인
echo?

결과

# code 확인
echo??

3.5.2 multy lines docstring

여러줄의 docstring을 넣을때는 멀티라인 문자열을 사용하여 작성 합니다.

def echo2(anything):
    """
    echo returns its input argument
    The operation is:
        1. print anything parameter
        2. return anything parameter
    param : anything : string
    return string
    """
    print("echo2 function : {}".format(anything))
    return anything

result2 = echo2("data_science")
result2

결과

echo2 function : data_science
data_science

3.5.3 help

help 함수를 사용하면 함수 선언과 docstring을 확인할수 있습니다.

help(echo)

결과

Help on function echo in module __main__:

echo(anything)
    echo returns its input argument

docstring만 확인 하고 싶으시면 아래와 같이 코딩하여 실행 하시면 됩니다.

print(echo.__doc__)

결과

echo returns its input argument

3.6 Scope - 범위

프로그램상에서 변수나 함수를 선언할 때 해당 함수는 변수를 사용할수 있는 영역 스코프가 존재 합니다. 스코프는 크게 두가지 영역으로 나뉘는데 어디에서나 사용이 가능한 global과 특정 영역에서만 사용이 가능한 local이 존재 합니다. global 변수를 전역 변수, local 변수를 지역 변수라고도 합니다.

3.6.1 Global

함수 밖에서 변수를 선언을 한후에 함수 안에서 출력하면 선언된 변수값이 나옵니다.

gv = 10

def print_gv():
    print(gv)

print_gv()

결과

10

함수 밖에 변수를 선언하면 global 영역에 선언이 되고 함수 안에 변수를 선언을 하면 local영역에 변수가 선언됩니다.

gv1, gv2 = 1, 2

def print_variable():
    gv1 = 10
    gv2 = 20
    print(gv1, gv2)

    # 리턴을 통해서 글로벌 영역에서 로컬 영역에있는 변수나 함수를 가져올수 있습니다.
    return gv1, gv2

print_variable()

gv1, gv2

결과

10 20
(1, 2)

global 변수를 보기 위해서는 globals()라는 함수를 사용하시면 볼수 있습니다.

gv1, gv2 = 1, 2

def print_globals():
    gv1 = 10
    gv2 = 20
    # print(globals())
    print(globals()["gv1"], globals()["gv2"], globals()["print_globals"])

print_globals()

결과

1 2 <function print_globals at 0x10c28fa60>

3.6.2 Local

local 변수를 보기 위해서는 locals 함수를 사용하시면 됩니다.

gv1, gv2 = 1, 2

def print_locals():
    gv1 = 10
    gv2 = 20
    def plus(a, b):
        return a + b
    print(locals())
    print(locals()["gv2"]) # gv2 - 20

print_locals()

결과

{'plus': <function print_locals.<locals>.plus at 0x10c28f510>, 'gv2': 20, 'gv1': 10}
20

함수도 지역 함수로 선언이 가능합니다. 지역 함수로 선언이 되면 선언된 영역에서만 사용이 가능하고, global 영역에서는 사용이 불가능 합니다.

3.6.3 local 영역에서 global 변수의 변경

local 영역에서 global 변수의 변경 하기 위해서는 global이라는 예약어를 사용하시면 됩니다. 아래 코드는 함수내에서 전역 변수를 변경하는 코드 입니다.

gv = 10

def change_gv(data):
    print("change_gv")
    gv = data
    print("local gv : {}".format(locals()["gv"]))
    print("global gv : {}".format(globals()["gv"]))

아래와 같이 결과를 출력해보면 지역변수로 gv:100이 생성되었고 gv global 변수는 수정이 되지 않음을 확인 할수 있습니다.

print(gv)

change_gv(100)

print(gv)

결과

10
change_gv
local gv : 100
global gv : 10
10

global 예약어를 사용하시면 함수 내에서 전역 변수의 값 변경이 가능합니다.

gv = 12

# 글로벌 영역에는 변수를 잘 선언하지 않고 상수를 선언하여 사용합니다.
PIE = 3.14 # 상수는 주로 대문자로 표기를 합니다.

def change_gv(data):
    global gv
    gv = data

print(gv)

change_gv(100)

print(gv)

결과

12
100

3.7 Inner Function

위에서 설명한것 처럼 전역 함수 내에 지역 함수를 선언할수 있습니다. 이때 전역 함수 내부에 선언된 진역 함수는 전역 함수 안에서만 사용이 가능합니다. 지역 함수의 실행 결과를 리턴 할수 있습니다. 이와 같은 방법으로 사용하는 이유는 전역 함수 외부에서 지역 함수를 사용할수 없게 만들어 함수의 기능을 숨길수 있으며(class에서 더 자세히 배웁니다.), 특정 함수가 전역으로 선언을 하지 않고 지역으로 선언을 하면 메모리를 절약 할 수 있습니다.

def outer(a, b):

    def inner(c, d):
        return c + d

    return inner(a, b)

outer(4, 7)

결과

11
def outer():

    def inner(c, d):
        return c + d

    return inner

# inner(1, 2)
outer()(1, 2)

결과

3

3.8 Lambda Function - 람다 함수

파라미터를 간단한 계산으로 리턴하는 함수는 람다함수를 이용하여 간단하게 만들수 있습니다.

lambda <parameters> : <return_value>
def sum_func(x, y):
    return x + y

sum_func(5, 6)

결과

11

위의 sum_func 함수를 lambda로 변경하면 아래와 같습니다.

sum_func2 = lambda x, y : x + y
print(sum_func2)
sum_func2(5, 6)

결과

11

람다 함수의 파라미터를 디폴트 파라미터로 설정할 수 있습니다.

sum_func3 = lambda x = 1, y = 2 : x + y
sum_func3(), sum_func3(5), sum_func3(5, 9)

람다 함수를 함수 호출시 아규먼트로 설정하여 함수의 파라미터로 사용이 가능합니다.

# callback
def sum_func4(f, x, y):
    # logic code
    return f(x, y)

sum_func4(lambda x, y : x + y, 5, 7)

결과

12

3.9 Map, Filter, Reduce

3.9.1 Map

map은 함수와 리스트 자료형을 받아 리스트의 각 요소에 함수를 적용한 결과를 리스트로 리턴하는 함수입니다. 사용은 map( <function>, <list>)와 같이 사용하며 결과는 리스트를 리턴 합니다.

1~10까지의 숫자 데이터가 있는 리스트의 모든 value에 3으로 나눈 나머지 리스트를 만드는 코드입니다.

number_list = list(range(1,11))
number_list

결과

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# 함수와 for 사용

def div_three(number):
    return number % 3

result = []

for number in number_list:
    result.append(div_three(number))

result

결과

[1, 2, 0, 1, 2, 0, 1, 2, 0, 1]
# function, map 사용

list(map(div_three, number_list))

결과

[1, 2, 0, 1, 2, 0, 1, 2, 0, 1]
# lambda, map 사용

list(map(lambda number: number % 3, number_list))

결과

[1, 2, 0, 1, 2, 0, 1, 2, 0, 1]
# map에 여러개의 파라미터 사용

ls1 = [1,2,3,4]
ls2 = [5,6,7,8]

def sum_func(a, b):
    return a + b

list(map(sum_func, ls1, ls2))

결과

[6, 8, 10, 12]
# sum_func 람다 function으로 변경

ls1 = [1,2,3,4]
ls2 = [5,6,7,8]

list(map(lambda x, y : x + y, ls1, ls2))

결과

[6, 8, 10, 12]
# map 함수 구현

ls1 = [1,2,3,4]
ls2 = [5,6,7]
ls3 = [9,10,11,12]

def map_func(func, *args):
    return [func(*datas) for datas in zip(*args)]


result = map_func(lambda *args : sum(args), ls1, ls2, ls3)
print(result)

결과

[15, 18, 21]

3.9.2 Filter

리스트 데이터에서 조건에 맞는 value 데이터만 남기는 Filter의 기능을 하는 함수입니다. Filter에 사용되는 함수는 bool 데이터 타입을 리턴 값으로 사용합니다. Filter에 사용되는 함수의 리턴 값이 True이면 값이 남아 있고 False이면 값을 제거 합니다.
사용은 filter( <function>, <list>)와 같이 사용하며 결과는 리스트를 리턴 합니다.

리스트에서 짝수 데이터만 가져오는 코드입니다.

def get_even(number_list):
    result = []
    for number in number_list:
        if not number % 2:
            result.append(number)
    return result

number_list = list(range(10))

print(number_list)

get_even(number_list)

결과

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 2, 4, 6, 8]

Filter를 사용한 리스트에서 짝수 데이터만 가져오는 코드 입니다.

def even(number):
    return number % 2 == 0

number_list = list(range(10))

print(number_list)

list(filter(even, number_list))

결과

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 2, 4, 6, 8]

help 명령을 사용하여 map과 filter의 docstring을 보면 아래와 같이 들어가는 아규먼트가 다른것을 확인 할수 있습니다. map은 여러개의 리스트가 아규먼트로 사용될수 있지만 filter는 하나의 리스트만 사용가능합니다.

map(func, *iterables) --> map object
filter(function or None, iterable) --> filter object
help(map)

결과

Help on class map in module builtins:

class map(object)
 |  map(func, *iterables) --> map object
 |  
 |  Make an iterator that computes the function using arguments from
 |  each of the iterables.  Stops when the shortest iterable is exhausted.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
help(filter)

결과

Help on class filter in module builtins:

class filter(object)
 |  filter(function or None, iterable) --> filter object
 |  
 |  Return an iterator yielding those items of iterable for which function(item)
 |  is true. If function is None, return the items that are true.
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  __next__(self, /)
 |      Implement next(self).
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
# filter 함수 구현

ls = range(1,11)

def filter_func(func, data_list):
    result = []
    for data in data_list:
        if func(data):
            result.append(data)    
    return result

list(filter_func(lambda number : number % 2 , ls))

결과

[1, 3, 5, 7, 9]

3.9.3 Reduce

리스트 데이터를 처음 부터 순서대로 특정 함수로 실행하여 결과를 누적시켜주는 함수입니다.
사용은 reduce( <function>, <list>)와 같이 사용하며 결과는 하나의 값으로 리턴 됩니다.

# functools 패키지에 reduce 모듈을 호출해야 사용이 가능합니다.
from functools import reduce

reduce를 사용하여 리스트의 숫자를 모두 더해주는 코드 입니다.

ls = [3, 1, 5, 2, 4]
reduce(lambda x, y: x + y, ls)

결과

15
# reduce를 사용하여 가장 큰수를 구하는 코드 입니다.

a = [3, 1, 5, 2, 4]
reduce(lambda x, y: x if x > y else y, a)

결과

5
# reduce 함수 구현

ls = [1, 2, 3, 4, 5]

def reduce_func(func, ls):

    result = ls[0]

    del ls[0] # [2, 3, 4, 5]

    for data in ls:
        result = func(result, data)

    return result

reduce_func(lambda num1, num2: num1 + num2 , ls)

결과

15

3.10 Decorlator - 데코레이터

코드를 바꾸지 않고 함수의 기능을 추가하거나 수정 싶을때 사용합니다. decorlator를 사용하여 공통되는 코드를 따로 묶어서 사용할수 있습니다. *args**kwargs를 이용하여 내부함수와 내부 인자로 사용합니다.

예를 들어 아래와 같은 함수를 만들때 code_1, code_3은 공통적인 코드로 공통적인 코드부분을 묶고 싶을때 사용할수 있습니다.

def a():
    code_1
    code_2
    code_3

def b():
    code_1
    code_4
    code_3

C() 함수는 데코레이터로 사용하기 위해 만든 함수로 위의 코드에서 A()와 B() 함수의 공통 코드인 code_1, code_3을 가지고 있습니다.

def C(func):
    def get_data():
        print("data")
    def wrapper(*args, **kwargs)
        code_1
        get_data()
        result = func(*args, **kwargs)
        code_3
        return result
    return wrapper

@C
def B():
    code_2

@C
def B():
    code_4

위에서 A 코드인 숫자 데이터 두개를 받아서 두 수를 더해서 리턴하는 함수를 작성하였습니다.

# A

def sum_int(a, b):
    return a + b

함수이름, 파라미터, 결과를 출력하고 결과를 리턴해주는 함수를 만들었습니다.

# C
def disp_func(func):
    def wrapper(*args, **kwargs):
        print("running function :", func.__name__)
        print("args :", args)
        print("kwargs :", kwargs)
        result = func(*args, **kwargs) # running func
        print("result :", result)
        return result
    return wrapper

데코레이터를 안쓰고 수동으로 선언하려면 아래와 같이 사용해야 합니다. 사용할때마다 disp_func에 함수를 넣어서 사용할수 있습니다.

new_sum_int = disp_func(sum_int)
new_sum_int(5, 7)

결과

running function : sum_int
args : (5, 7)
kwargs : {}
result : 12
12

하지만 sum_int 함수는 항상 데이터가 디스플레이 되게 하려면 아래 코드와 같이 데코레이터를 사용하는것이 더 심플합니다. sum_int() 함수를 호출할때 disp_func이 추가되어 실행 됩니다.

@disp_func
def sum_int(a, b):
    return a + b

sum_int(5, 7)

결과

running function : sum_int
args : (5, 7)
kwargs : {}
result : 12
12

데코레이터를 사용하여 함수의 실행시간을 알려주는 timer 함수를 작성하는 코드 입니다.

import time
def timer(func):
    def wrapper(*args, **kwargs): # 함수 정의부 packing
        start_time = time.time()
        result = func(*args, **kwargs) # 함수 호출부 unpacking
        end_time = time.time()
        print("time : {time}".format(time = end_time - start_time))
        return result
    return wrapper # 새로운 함수를 만들고, 그 함수를 return 하는 함수 => decorator 함수

위에서 만든 timer 데코레이터로 사용할 함수를 이용하여 세가지 방법의 리스트의 숫자를 더하는 함수가 실행되는데 걸리는 시간을 확인하는 코드 입니다.

@timer
def sum_func_1(start_number, last_number):
    data = range(start_number, last_number + 1)
    return sum(data)

@timer
def sum_func_2(start_number, last_number):
    result = 0
    for num in range(start_number, last_number + 1):
        result += num
    return result

def sum_func_3(start_number, last_number):
    number_list = [ num for num in range(start_number, last_number + 1)]
    return sum(number_list)
sum_func_1(1, 10000)

결과

time : 0.0003509521484375
50005000

데코레이터를 사용하여 관리자 계정이면 패스워드를 출력해주는 코드입니다.

admin_ls = ["pdj", "dss"]
pw = "asdfqwer"
def admin(func):
    def wrapper(*args, **kwargs):
        flag = False
        result = func(*args, **kwargs)
        if result in admin_ls:
            print("allow permission!")
            print("password :", pw)
            flag = True
        else:
            print("you are not admin!")
        return flag
    return wrapper
@admin
def input_id():
    return input("insert id : ")
input_id()

결과

insert id : dss
allow permission!
password : asdfqwer
True
input_id()

결과

insert id : qqq
you are not admin!
False
Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

잘 보고갑니다. 팔로우 할께요.
그리고 kr-dev 태그도 사용해보세요^^

Congratulations @radajin! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Vote for @Steemitboard as a witness to get one more award and increased upvotes!